I have this bash script:

for opt in string1 string2 string3 ... string99
do somestuff

It works, but I would like to replace the explicit listing of my strings with a file which actually contains all the strings; something like this:

for opt in $strings
do somestuff

How should I do this?


5 Answers 5


while read VAR is probably best here, as it handles per-line input. You can redirect it from a file, e.g.:

while IFS= read -r THELINE; do
  echo "..$THELINE"
done </path/to/file

That'll give you each line prepended with ".."

For your example case:

while IFS= read -r opt; do
  #somestuff $opt
done </path/to/file

See Why is `while IFS= read` used so often, instead of `IFS=; while read..`? for explanations.

  • 3
    Putting cats into pipes is bad behavior from a memory management point of view. especially when using "filter" built-in commands. @rush's answer is better, and what I use on a daily basis.
    – Didi Kohen
    Sep 19, 2012 at 14:15
  • 3
    It also puts the while loop into a subshell, which can cause confusing behaviour if update variable values in the loop (the values will disappear when the subshell exits) Sep 19, 2012 at 15:48
  • If somestuff includes another read, this seems not to work properly.
    – Raphael
    Feb 23, 2013 at 19:04
  • @raphael - in what way does it not "work properly"? I just tried nested reads and it works fine.
    – IBBoard
    Feb 24, 2013 at 13:52
  • @IBBoard See this new question.
    – Raphael
    Feb 24, 2013 at 15:59
while IFS= read -r opt
done < file_with_string

See Why is `while IFS= read` used so often, instead of `IFS=; while read..`? for explanations.


The while IFS= read -r line; do ...; done < aFile is the best answer

If your strings do not contain whitespace or \[*?, you could do

for word in $(< aFile); do something; done

$(< file) is a bash feature that reads the file (like cat except without having to spawn a new process).


Why don't you use the readarray builtin (requires bash >= 4.0)?

readarray < FileNameFromWhereToLoad    # push every line of 
                                       # 'FileNameFromWhereToLoad' onto 
                                       # $MAPFILE by default

for i in $MAPFILE ; do
    echo $i
  • This also requires newer version of bash.
    – Didi Kohen
    Sep 19, 2012 at 14:12
  • i wouldn't call bash >= 4 new (release date was February 2009)! Sep 19, 2012 at 14:31
  • 2
    Since he's using 2.05a, it is newer than what the asker has. In addition, some companies have avoided GPLv3 versions of bash, so do not have it in their system.
    – Didi Kohen
    Sep 19, 2012 at 14:36
  • Do you have a crystal ball around? But yes, i agree with you about your last statement, i will add the version requirement of readarray to my answer. Sep 19, 2012 at 14:39
  • 1
    I didn't read that. Thanks for the hint ;) Maybe my answer is of value for somebody who will search for an answer for a similar question!? Sep 19, 2012 at 14:56

My advice:

  declare -a LINES
  mapfile -t LINES
  for line in "${LINES[@]}"
  • Syntax error: bad substitution Sep 19, 2012 at 13:34
  • 1
    Works for me. @michelemarcon, is your bash 4.00 or never?
    – manatwork
    Sep 19, 2012 at 13:36
  • GNU bash, version 2.05a.0(1)-release (arm-unknown-linux-gnu) Sep 19, 2012 at 13:42
  • 5
    That is ancient. Please specify this requirement in the question.
    – manatwork
    Sep 19, 2012 at 13:46
  • osx 10.12.2 has ancient bash ; $ bash --version GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin16) Copyright (C) 2007 Free Software Foundation, Inc. Dec 23, 2016 at 16:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.