Bash while loop behave differently in file vs direct execution [duplicate] - bash

This question already has answers here:
Loop through an array of strings in Bash?
(21 answers)
Closed 2 years ago.
The following is the sample while loop.
res=()
lines=$'first\nsecond\nthird'
while read line; do
res+=("$line")
done <<< "$lines"
echo $res
When i run this directly in terminal im getting the following out put.
first second third
But when run the same script by saving it to a file. Then im getting the following out put.
first
Why is it behaving differently?
Note: I tested with and without shebang in file. result is same.

If you have a string with embedded newlines and want to turn it into an array split on them, use the mapfile builtin:
$ mapfile -t res <<<$'first\nsecond\nthird'
$ printf "%s\n" "${res[#]}"
first
second
third

Related

In shell script, how do I use the contents of a file as a parameter [duplicate]

This question already has answers here:
Need to assign the contents of a text file to a variable in a bash script
(4 answers)
Closed 4 years ago.
Suppose in dir.txt I have the following content:
test-dir
I try to use that as a parameter as follows:
echo dir.txt | cp * $1
I want the above to be the equivalent of:
cp * test-dir
What am I doing wrong?
You are giving the string "dir.txt" to a program that does not accept any input by stdin.
You are looking for the following syntax:
cp * "$(<dir.txt)"
$() runs the command inside parenthesis and substitutes its results in its position in the command line. The < is a shorthand to read a file (a cat would also work). The quotes are to avoid problems with spaces.
You can get content of file to variable:
file1=$(cat dir.txt)
echo $file1
Results:
test-dir

How to read the last characters not ending with a newline from a stream [duplicate]

This question already has answers here:
Reading input files by line using read command in shell scripting skips last line
(5 answers)
Shell script read missing last line
(7 answers)
Closed 4 years ago.
I have a script called test.sh which processes the standard input line by line like this:
#!/usr/bin/env bash
echo "start"
while IFS= read -r line; do
echo "processing[$line]"
done < /dev/stdin
echo "done"
The problem with this is, it doesn't process the characters between the last newline and the eof.
printf $'line 1\nline 2\nlast chars' | test.sh
will output
start
processing[line 1]
processing[line 2]
done
The reason I read line by line is that I need to inspect the first line and in some cases I want to remove it from the output stream.
How can I process these last characters? I've looked into read -n but then I would need to supply how many characters to expect at a maximum and I rather don't build in limits.
Also: I wouldn't know where to put this statement in the while-loop. I'm on the macOS platform.

Bash "while read loop" does not properly recognize variables [duplicate]

This question already has answers here:
bash variable interpolation separate variables by a hyphen or underscore
(3 answers)
Error in string Concatenation in Shell Scripting
(3 answers)
Closed 5 years ago.
I am using MacBook pro terminal to execute a shell script. It loops through a text file and create filenames based on each line in the file.
#!/bin/bash
year=2010
list=list_test.txt
mydir=thisdir
i=1 # counter
while read line
do
echo $i $line
file1=`echo $mydir/file_$year_$line_test.tif`
file2=`echo $mydir/file_$year_$line_test.tif`
echo $file1 $file2
i=$(($i+1))
done < $list
However, the output is peculiar:
1 17019
thisdir/file_.tif thisdir/file_.tif
2 17029
thisdir/file_.tif thisdir/file_.tif
3 17039
thisdir/file_.tif thisdir/file_.tif
Within the loop, bash does not recognize some variables, like "year" which is a global, and "line" which is read from the text file. The text file is as below:
17019
17029
17039
Another script with exactly the same manner works very well. This is mysterious to me now.
Any help or comments are extremely appreciated! Thanks very much!
_ is a valid character for an identifier, but you want to use it as a literal character in the file name. You need to use the full form of parameter expansion, ${x} instead of $x.
(Also, the command substitution isn't necessary.)
file1=$mydir/file_${year}_${line}_test.tif
file2=$mydir/file_${year}_${line}_test.tif

I can't change the value of line_count at the right of pipe , why? [duplicate]

This question already has answers here:
While-loop subshell dilemma in Bash
(4 answers)
Closed 8 years ago.
I occured a problem and I can't find why does it run.
The follow codes is both used to count the line of file 'file.in' , but the first can't change the value of $line_count
The first code is :
#!/bin/bash
line_count=0
cat file.in | while read line; do
let ++line_count
done
echo $line_count
the second code is :
#!/bin/bash
line_count=0
while read line; do
let ++line_count
done < file.in
echo $line_count
Due to use of pipe your first code sample is executing while loop in a sub-shell hence changes made in line_count variable get lost after sub shell exits.

Passing file to shell script multiple times [duplicate]

This question already has answers here:
rewinding stdin in a bash script
(4 answers)
Closed 7 years ago.
I have a shell script where I pass a txt file to the script as follows:
./run.sh < list.txt
Within the script, I am doing a "while read LIST do ... end"
It all works well, and the script executes using the list.
However, now I want to have a second while read LIST do ... end in the same shell script. I want it to read again from the original list I'm passing it on execution, but it doesn't work. It reads the list.txt file for the first loop, but not the second.
What do I do to make the script read list.txt each time I'm asking for it?
You can't read stdin twice. Try passing list.txt on the command-line rather than redirecting it.
./run.sh list.txt
Then in your script:
while read LINE; do
...
done < "$1"
while read LINE; do
...
done < "$1"
Alternatively, save the contents of stdin off the first time you read through it. For instance:
# First loop, save stdin in an array.
LINES=()
while read LINE; do
LINES+=("$LINE")
...
done
# Second loop, iterate over the array.
for LINE in "${LINES[#]}"; do
...
done

Resources