2

I have a python script that outputs a long list of files.

The script is ran from bash, and at the end i need to assign the last three files each to a specific variables.

I'm able to capture the output into a bash Variable, but then when I try to pipe it to 'tail' command, it seems to be one line only?

I've tried tips i've seen around, but can't seem to figure out how to work on an individual line basis with output.

it seems that any python output will be interpreted as one line?


echo "TESTING sample multi-line python output"
OUTPUT=$(python -c "for i in range(5): print(i)")

# check output
echo "$OUTPUT"

variable3=$(echo $OUTPUT | tail -1)

echo "VARIABLE CAPTURED"
echo $variable3

here is what i get: enter image description here

But I actually need to capture variable1 as 3rd line from end, variable2 as 2nd line from end and variable3 as first line from the end

So that in the above example at the end the desired result is:

variable1 = 2
variable2 = 3
variable3 = 4

in order to pass these variables to next stage of the script...

Walter A
  • 19,067
  • 2
  • 23
  • 43
yulGM
  • 894
  • 1
  • 5
  • 14
  • 1
    You need to quote `"$OUTPUT"`, otherwise it is split on IFS (whitespace, including new line), and `echo` prints each argument, seperated by a space. `python -c ... | tail -n3`. – dan Feb 12 '22 at 23:37

4 Answers4

2

You have to get quoting right, this will assign 4 to variable3:

variable3="$(echo "$OUTPUT" | tail -1)"
echo "$variable3"

Similarly, to get variable2:

variable2="$(echo "$OUTPUT" | tail -2 | head -1)"

And variable1:

variable1="$(echo "$OUTPUT" | tail -3 | head -1)"

To sum up, your script should be:

#!/usr/bin/env bash

echo "TESTING sample multi-line python output"
OUTPUT="$(python -c "for i in range(5): print(i)")"

# check output
echo "$OUTPUT"

variable3="$(echo "$OUTPUT" | tail -1)"
variable2="$(echo "$OUTPUT" | tail -2 | head -1)"
variable1="$(echo "$OUTPUT" | tail -3 | head -1)"

echo variable1: "$variable1"
echo variable2: "$variable2"
echo variable3: "$variable3"
Arkadiusz Drabczyk
  • 11,227
  • 2
  • 25
  • 38
2

So I can't explain why this goes that way, but it seems that pythons print separates lines by '\n' and tail separates lines by \n\r. So I got this working by changing some lines

echo "TESTING sample multi-line python output"
OUTPUT=$(python -c "for i in range(5): print(str(i) + '\n\r', end='')")

# check output
echo "$OUTPUT"

variable3=$(echo $OUTPUT | tail -1)

echo "VARIABLE CAPTURED"
echo $variable3

This will result in

TESTING sample multi-line python output
0
1
2
3
4

VARIABLE CAPTURED
4 
sudden_appearance
  • 1,968
  • 1
  • 4
  • 15
2

One option could be to tail -3 and read those last lines into an array:

#!/bin/bash

echo "TESTING sample multi-line python output"
readarray -t arr <<< "$(python -c "for i in range(5): print(i)" | tail -3)"

echo "VARIABLE CAPTURED"
echo "${arr[0]}"
echo "${arr[1]}"
echo "${arr[2]}"

echo "VIA LOOP"
for var in "${arr[@]}"; do
    echo "$var"
done

Output

TESTING sample multi-line python output
VARIABLE CAPTURED
2
3
4
VIA LOOP
2
3
4

If you really need the full output saved before extracting the three last lines:

OUTPUT="$(python -c "for i in range(5): print(i)")"
readarray -t arr <<< "$(echo "$OUTPUT" | tail -3)"
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
0

When you don't need your current environment, you can use set.
When you want to use the the variable names variable# you need to copy them.

set -- $( python -c "for i in range(5): print(i)"| tail -3)
variable1="$1"; variable2="$2"; variable3="$3"
# In my environment I can check the results with
set | grep "^variable.="

An other option is reading them into the variables:

read -rd "\n" variable1 variable2 variable3 < <(
  python -c "for i in range(5): print(i)"| tail -3
)
Walter A
  • 19,067
  • 2
  • 23
  • 43