Need to assign the contents of a text file to a variable in a bash script
I am very new to making bash scripts, but my goal here is to take a .txt file I have and assign the string of words in the txt file to a variable. I have tried this (no clue if I am on the right track or not).
#!/bin/bash FILE="answer.txt" file1="cat answer.txt" print $file1
Warning: unknown mime-type for "cat" -- using "application/octet-stream" Error: no such file "cat" Error: no "print" mailcap rules found for type "text/plain"
#!/bin/bash FILE="answer.txt" file1=$(cat answer.txt) print $file1
Warning: unknown mime-type for "This" -- using "application/octet-stream" Warning: unknown mime-type for "text" -- using "application/octet-stream" Warning: unknown mime-type for "string" -- using "application/octet-stream" Warning: unknown mime-type for "should" -- using "application/octet-stream" Warning: unknown mime-type for "be" -- using "application/octet-stream" Warning: unknown mime-type for "a" -- using "application/octet-stream" Warning: unknown mime-type for "varible." -- using "application/octet-stream" Error: no such file "This" Error: no such file "text" Error: no such file "string" Error: no such file "should" Error: no such file "be" Error: no such file "a" Error: no such file "varible."
When I enter cat answer.txt it prints out this text string should be a varible like it should but, I still can’t get the bash to do that with the varible.
Read a file line by line assigning the value to a variable [duplicate]
Can those questions maybe be merged somehow? Both have some really good answers that highlight different aspects of the problem, the bad answers have in-depth explanations in the comments what’s bad about them, and as of now you cannot really get a whole overview on what to consider, from the answers of one single question from the pair. It would be helpful to have all of it in one spot, rather than splotted over 2 pages.
10 Answers 10
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= (or IFS='' ) 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
There is a caveat with this method. If anything inside the while loop is interactive (e.g. reads from stdin), then it will take its input from $1. You will not be given a chance to enter data manually.
Of note - some commands break (as in, they break the loop) this. For example, ssh without the -n flag will effectively cause you to escape the loop. There's probably a good reason for this, but it took my a while to nail down what was causing my code to fail before I discovered this.
@OndraŽižka, that's caused by ffmpeg consuming stdin. Add grumble re: advising a .sh extension. Executables on UNIX don't typically have extensions at all (you don't run ls.elf ), and having a bash shebang (and bash-only tooling such as [[ ]] ) and an extension implying POSIX sh compatibility is internally contradictory.
I encourage you to use the -r flag for read which stands for:
-r Do not treat a backslash character in any special way. Consider each backslash to be part of the input line.
I am citing from man 1 read .
Another thing is to take a filename as an argument.
#!/usr/bin/bash filename="$1" while read -r line; do name="$line" echo "Name read from file - $name" done < "$filename"
@TranslucentCloud, if this worked and the accepted answer didn't, I suspect that your shell was sh , not bash ; the extended test command used in the || [[ -n "$line" ]] syntax in the accepted answer is a bashism. That said, that syntax actually has pertinent meaning: It causes the loop to continue for the last line in the input file even if it doesn't have a newline. If you wanted to do that in a POSIX-compliant way, you'd want || [ -n "$line" ] , using [ rather than [[ .
That said, this does still need to be modified to set IFS= for the read to prevent trimming whitespace.
Using the following Bash template should allow you to read one value at a time from a file and process it.
while read name; do # Do what you want to $name done < filename
@CalculusKnight, it only "worked" because you didn't use sufficiently interesting data to test with. Try content with backslashes, or having a line that contains only * .
@Matthias, assumptions that eventually turn out to be false are one of the largest sources of bugs, both security-impacting and otherwise. The largest data loss event I ever saw was due to a scenario someone assumed would "literally never come up" -- a buffer overflow dumping random memory into a buffer used to name files, causing a script that made assumptions about which names could possibly ever occur to have very, very unfortunate behavior.
@Matthias, . and that's especially true here, since code samples shown at StackOverflow are intended to be used as teaching tools, for folks to reuse the patterns in their own work!
@Matthias, I utterly disagree with the claim that "you should only devise your code for data you expect". Unexpected cases are where your bugs are, where your security vulnerabilities are -- handling them is the difference between slapdash code and robust code. Granted, that handling doesn't need to be fancy -- it can just be "exit with an error" -- but if you have no handling at all, then your behavior in unexpected cases is undefined.
#! /bin/bash cat filename | while read LINE; do echo $LINE done
Nothing against the other answers, maybe they are more sofisticated, but I upvote this answer because it's simple, readable and is enough for what I need. Note that, for it to work, the text file to be read must end with a blank line (i.e. one needs to press Enter after the last line), otherwise the last line will be ignored. At least that is what happened to me.
And the quoting is broken; and you should not use uppercase variable names because those are reserved for system use.
@AntonioViniciusMenezesMedei, . moreover, I've seen folks sustain financial losses because they assumed these caveats would never matter to them; failed to learn good practices; and then followed the habits they were used to when writing scripts that managed backups of critical billing data. Learning to do things right is important.
Another problem here is that the pipe opens a new subshell, i.e. all variables set inside the loop can't be read after the loop finished.
filename=$1 IFS=$'\n' for next in `cat $filename`; do echo "$next read from $filename" done exit 0
If you have set IFS differently you will get odd results.
This is a horrible method. Please don't use it unless you want to have problems with globbing that will take place before you realize it!
@MUYBelgium did you try with a file that contains a single * on a line? Anyway, this is an antipattern. Don't read lines with for.
@OndraŽižka, the read approach is the best-practices approach by community consensus. The caveat you mention in your comment is one that applies when your loop runs commands (such as ffmpeg ) that read from stdin, trivially solved by using a non-stdin FD for the loop or redirecting such commands' input. By contrast, working around the globbing bug in your for -loop approach means making (and then needing to reverse) shell-global settings changes.
@OndraŽižka, . moreover, the for loop approach you use here means that all content has to be read in before the loop can start executing at all, making it entirely unusable if you're looping over gigabytes of data even if you have disabled globbing; the while read loop needs to store no more than a single line's data at a time, meaning it can start executing while the subprocess generating content is still running (thus being usable for streaming purposes), and also has bounded memory consumption.
Unix Output file content to a variable
I need to read the contents of a text file and assign the content to variable to be available in the script. I see many questions on the same topic but my script does not work.
export CONTENT_FROM_FILE="/home/me/test.sql" echo $CONTENT_FROM_FILE VAR=$(while IFS= read line;do echo "$line";done < "$CONTENT_FROM_FILE") echo $VAR
VAR=$(while IFS= read line;do echo "$line";done < "/home/me/test.sql") echo $VAR
That's really strange. CONTENT_FROM_FILE should work even without export statement, and your code works for me perfectly (that is, if you intend to read the whole file in one line). Could you give an example of file for which it fails, and the output you're getting?
@learninghuman: Do provide feedback on the provided answers to help you out further. Or alternatively, if it helped solve your problem, do accept/up-vote the answer for it to be helpful for others.
2 Answers 2
If you are in bash , you can do command-substitution to achieve the same and avoid use of cat or a redundant while-loop
To see this in action, am filling a sample file with below contents:-
$ cat file Hi This is a test file - First line Hi This is a test file - Second line Hi This is a test file - Third line
Am running a script which reads this file and prints it to stdout
$ cat script.sh #!/bin/bash myfileContent="$(
$ ./script.sh Hi This is a test file - First line Hi This is a test file - Second line Hi This is a test file - Third line