Read a file line by line assigning the value to a variable
The following reads a file passed as an argument line by line:
while IFS= read -r line; do
echo "Text read from file: $line"
done < my_filename.txt
This is the standard form for reading lines from a file in a loop. Explanation:
IFS=
(orIFS=''
) prevents leading/trailing whitespace from being trimmed.-r
prevents backslash escapes from being interpreted.
Or you can put it in a bash file helper script, example contents:
#!/bin/bash
while IFS= read -r line; do
echo "Text read from file: $line"
done < "$1"
If the above is saved to a script with filename readfile
, it can be run as follows:
chmod +x readfile
./readfile filename.txt
If the file isn’t a standard POSIX text file (= not terminated by a newline character), the loop can be modified to handle trailing partial lines:
while IFS= read -r line || [[ -n "$line" ]]; do
echo "Text read from file: $line"
done < "$1"
Here, || [[ -n $line ]]
prevents the last line from being ignored if it doesn't end with a \n
(since read
returns a non-zero exit code when it encounters EOF).
If the commands inside the loop also read from standard input, the file descriptor used by read
can be chanced to something else (avoid the standard file descriptors), e.g.:
while IFS= read -r -u3 line; do
echo "Text read from file: $line"
done 3< "$1"
(Non-Bash shells might not know read -u3
; use read <&3
instead.)
Read line by line in Bash script
What you have is piping the text "cat test"
into the loop.
You just want:
cat test | \
while read CMD; do
echo $CMD
done
How to read file line by line in Bash script?
Your code leads me to believe you want each line in one variable.
Try this script (I know this can be done easier and prettier, but this is a simple and readable example):
#!/bin/bash
file="cam.txt"
while read -r line
do
printf 'Line: %s\n' "$line"
current=$line
last=$current
secondlast=$last
printf 'Loop: %s %s %s\n' "$current" "$last" "$secondlast"
done < $file
printf 'After: %s %s %s\n' "$current" "$last" "$secondlast"
Simpler version:
{ read -r first; read -r second; read -r third; } <cam.txt
printf 'After: %s %s %s\n' "$first" "$second" "$third"
Read line by line from standard input Bash
- How do I read line by line from standard input in Bash? Until now I
used "read string" but I do not think that it reads a line at a time.
The prototype for read
is:
read [options] name[s ...]
read
will read a line
of input into name name1 name2 ...
splitting the line based on the contents of the Internal Field Separator (IFS
). The default for IFS
is ' \t\n'
(that is space
tab
newline
). If you only provide a single variable to read
, you will read the entire line into that variable (unless you have set a new delimiter with the -d
option to read
). If you provide more than one variable, (e.g. read -r name name1
) word splitting will occur based on the current value of IFS
. Meaning if you provide the string hello world
to:
read -r name
name="hello world"
. On the other hand, if you provide the same string to:
read -r name name1
name="hello"
, name1="world"
. What if you have excess words in the line but only 2 variables? Say your string is now "hello big wide world"
, what happens with:
read -r name name1
name="hello"
, name1="big wide world"
. The words in string
are assigned to your variables in order and if there are insufficient variables to hold each word in the string, the last variable will contain all remaining words in the string not previously assigned.
You change how word splitting occurs by altering IFS
. Take a close look at the answer provided by anubhava for an example. You are free to specify any character you would like the words to be split on. (helpful in say parsing a csv
file to set IFS=$',\n'
and have the words split on ','
instead of space)
To ensure you read an entire line into a variable, you can provide only a single variable to read
and set IFS='$\n'
to ensure word splitting only occurs on newline
. (Note: providing the change as part of the while
loop limits the IFS
alteration to the scope of that loop. For example:
while IFS='$\n' read -r line; do
# do whatever with line
done
Will ensure that each line on stdin
will be read into line
while preserving normal word-splitting outside the loop. Inside the loop you can then add each line to an array as anubhava shows in his answer. (to preserve all whitespace IFS=
is used)
Looping through the content of a file in Bash
One way to do it is:
while read p; do
echo "$p"
done <peptides.txt
As pointed out in the comments, this has the side effects of trimming leading whitespace, interpreting backslash sequences, and skipping the last line if it's missing a terminating linefeed. If these are concerns, you can do:
while IFS="" read -r p || [ -n "$p" ]
do
printf '%s\n' "$p"
done < peptides.txt
Exceptionally, if the loop body may read from standard input, you can open the file using a different file descriptor:
while read -u 10 p; do
...
done 10<peptides.txt
Here, 10 is just an arbitrary number (different from 0, 1, 2).
read file line by line and sum each line individually
You can do this fairly easily in bash itself making use of built-in parameter expansions to trim leading zeros from the beginning of each line in order to sum the digits for odd / even.
When reading from a file (either a named file or stdin
by default), you can use the initialization with default to use the first argument (positional parameter) as the filename (if given) and if not, just read from stdin
, e.g.
#!/bin/bash
infile="${1:-/dev/stdin}" ## read from file provide as $1 or stdin
Which you will use infile
with your while
loop, e.g.
while read -r line; do ## loop reading each line
...
done < "$infile"
To trim the leading zeros, first obtain the substring of leading zeros trimming all digits from the right until only zeros remain, e.g.
leading="${line%%[1-9]*}" ## get leading 0's
Now using the same type parameter expansion with #
instead of %%
trim the leading zeros substring from the front of line
saving the resulting number in value
, e.g.
value="${line#$leading}" ## trim from front
Now zero your sum
and loop over the digits in value
to obtain the sum of digits:
for ((i=0;i<${#value};i++)); do ## loop summing digits
sum=$((sum + ${value:$i:1}))
done
All that remains is your even / odd test. Putting it altogether in a short example script that intentionally outputs the sum of digits in addition to your wanted "odd"
/ "even"
output, you could do:
#!/bin/bash
infile="${1:-/dev/stdin}" ## read from file provide as $1 or stdin
while read -r line; do ## read each line
[ "$line" -eq "$line" 2>/dev/null ] || continue ## validate integer
leading="${line%%[1-9]*}" ## get leading 0's
value="${line#$leading}" ## trim from front
sum=0 ## zero sum
for ((i=0;i<${#value};i++)); do ## loop summing digits
sum=$((sum + ${value:$i:1}))
done
printf "%s (sum=%d) - " "$line" "$sum" ## output line w/sum
## (temporary output)
if ((sum % 2 == 0)); then ## check odd / even
echo "even"
else
echo "odd"
fi
done < "$infile"
(note: you can actually loop over the digits in line
and skip removing the leading zeros substring. The removal ensure that if the whole value is used it isn't interpreted as an octal value -- up to you)
Example Use/Output
Using a quick process substitution to provide input of 001 - 020
on stdin
you could do:
$ ./sumdigitsoddeven.sh < <(printf "%03d\n" {1..20})
001 (sum=1) - odd
002 (sum=2) - even
003 (sum=3) - odd
004 (sum=4) - even
005 (sum=5) - odd
006 (sum=6) - even
007 (sum=7) - odd
008 (sum=8) - even
009 (sum=9) - odd
010 (sum=1) - odd
011 (sum=2) - even
012 (sum=3) - odd
013 (sum=4) - even
014 (sum=5) - odd
015 (sum=6) - even
016 (sum=7) - odd
017 (sum=8) - even
018 (sum=9) - odd
019 (sum=10) - even
020 (sum=2) - even
You can simply remove the output of "(sum=X)"
when you have confirmed it operates as you expect and redirect the output to your new file. Let me know if I understood your question properly and if you have further questions.
Related Topics
How to Delete the First Column ( Which Is in Fact Row Names) from a Data File in Linux
Redirecting Command Output to a Variable in Bash Fails
Sort Logfile by Timestamp on Linux Command Line
Rename Files to Md5 Sum + Extension (Bash)
Why Does Ps O/P List the Grep Process After the Pipe
Syntax of for Loop in Linux Shell Scripting
How to De-Optimize the Linux Kernel to and Compile It with -O0
How to Run a Docker Container in Aws Elastic Beanstalk with Non-Default Run Parameters
How to Remove All Special Characters in Linux Text
Generating a CSV List from Linux 'Ps'
How to Programmatically Create Videos
How to Convert Spaces to Tabs in Vim or Linux
How to Get Exit Status of a Shell Command Used in Gnu Makefile
Getting Disconnection Notification Using Tcp Keep-Alive on Write Blocked Socket