I want to use command substitution in a for loop in my bash script like this:
for file in `ls`; do
echo "$file"
tar xzvf "$file"
done
The problem is upon extracting each file and in the next iteration, ls would be executed again so the for loop iterate over new collection. I decided to capture ls output before starting the loop and use that in loop:
files=`ls`
for file in ${files}; do
echo "$file"
tar xzvf "$file"
done
But it seems instead of running ls and store the result in $files, shell just replaces ${files} with ls and I'm at the same point as I was in first code example.
How can I force shell to run ls command in files=ls part of code?
Update
I'm on my Ubuntu 16.04 laptop:
$ uname -a
Linux laptop 4.4.0-131-generic #157-Ubuntu SMP Thu Jul 12 15:51:36 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
and GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)
There is a folder in my home named test with two simple compressed files plus my script:
test
├── a.tar.gz
├── b.tar.gz
└── script.sh
script.sh content:
#!/usr/bin/env bash
files=`ls`
for file in ${files}; do
echo "$file"
tar xzvf "$file"
done
And here is the output of bash -x script.sh from inside of test folder:
++ ls
+ files='a.tar.gz
b.tar.gz
script.sh'
+ for file in '${files}'
+ echo a.tar.gz
a.tar.gz
+ tar xzvf a.tar.gz
a
+ for file in '${files}'
+ echo b.tar.gz
b.tar.gz
+ tar xzvf b.tar.gz
b
+ for file in '${files}'
+ echo script.sh
script.sh
+ tar xzvf script.sh
gzip: stdin: not in gzip format
tar: Child returned status 1
tar: Error is not recoverable: exiting now
And finally bash script.sh (after deleting extracted files manually) output:
a.tar.gz
a
b.tar.gz
b
script.sh
gzip: stdin: not in gzip format
tar: Child returned status 1
tar: Error is not recoverable: exiting now
Thanks for your help and patience.
forloops do not re-run the command on each iteration, andfiles=`ls`most definitely captures the output oflsonce on assignment and not on each reference. Can you please run your script withbash -x yourscriptto generate a debug log, and update your question with the complete and unabbreviated output?for zipfile in *.tar.gz; do tar xzvf "$zipfile" && mv "$zipfile" "$zipfile.done"; doneso that it explicitly picks up only tar.gz files and is more robust, as well as immune to any issue if you rerun the script?lsis a common anti-pattern. Consider instead using an array variable:files=(*); for file in "${files[@]}"; do- that's less fragile with filenames containing spaces or newlines, for example. Oh, and don't forget to runshellcheckon your scripts!