How to loop over files in directory and change path and add suffix to filename
A couple of notes first: when you use Data/data1.txt
as an argument, should it really be /Data/data1.txt
(with a leading slash)? Also, should the outer loop scan only for .txt files, or all files in /Data? Here's an answer, assuming /Data/data1.txt
and .txt files only:
#!/bin/bash
for filename in /Data/*.txt; do
for ((i=0; i<=3; i++)); do
./MyProgram.exe "$filename" "Logs/$(basename "$filename" .txt)_Log$i.txt"
done
done
Notes:
/Data/*.txt
expands to the paths of the text files in /Data (including the /Data/ part)$( ... )
runs a shell command and inserts its output at that point in the command linebasename somepath .txt
outputs the base part of somepath, with .txt removed from the end (e.g./Data/file.txt
->file
)
If you needed to run MyProgram with Data/file.txt
instead of /Data/file.txt
, use "${filename#/}"
to remove the leading slash. On the other hand, if it's really Data
not /Data
you want to scan, just use for filename in Data/*.txt
.
While loop stops reading after the first line in Bash
The problem is that do_work.sh
runs ssh
commands and by default ssh
reads from stdin which is your input file. As a result, you only see the first line processed, because the command consumes the rest of the file and your while loop terminates.
This happens not just for ssh
, but for any command that reads stdin, including mplayer
, ffmpeg
, HandBrakeCLI
, httpie
, brew install
, and more.
To prevent this, pass the -n
option to your ssh
command to make it read from /dev/null
instead of stdin. Other commands have similar flags, or you can universally use < /dev/null
.
bash shell nested for loop
The question does not contain a nested loop, just a single loop. But THIS nested version works, too:
# for i in c d; do for j in a b; do echo $i $j; done; done
c a
c b
d a
d b
bash - run .sh in loop
Expand $f
:
then echo "FOUND" && sh "$f"/run.sh && break
Also depending on what run.sh
does, you may want to use source, rather than running it in a new separate shell:
then echo "FOUND" && source "$f$"/run.sh && break
Loop through all the files with a specific extension
No fancy tricks needed:
for i in *.java; do
[ -f "$i" ] || break
...
done
The guard ensures that if there are no matching files, the loop will exit without trying to process a non-existent file name *.java
.
In bash
(or shells supporting something similar), you can use the nullglob
option
to simply ignore a failed match and not enter the body of the loop.
shopt -s nullglob
for i in *.java; do
...
done
Some more detail on the break
-vs-continue
discussion in the comments. I consider it somewhat out of scope whether you use break
or continue
, because what the first loop is trying to do is distinguish between two cases:
*.java
had no matches, and so is treated as literal text.*.java
had at least one match, and that match might have included an entry named*.java
.
In case #1, break
is fine, because there are no other values of $i
forthcoming, and break
and continue
would be equivalent (though I find break
more explicit; you're exiting the loop, not just waiting for the loop to exit passively).
In case #2, you still have to do whatever filtering is necessary on any possible matches. As such, the choice of break
or continue
is less relevant than which test (-f
, -d
, -e
, etc) you apply to $i
, which IMO is the wrong way to determine if you entered the loop "incorrectly" in the first place.
That is, I don't want to be in the position of examining the value of $i
at all in case #1, and in case #2 what you do with the value has more to do with your business logic for each file, rather than the logic of selecting files to process in the first place. I would prefer to leave that logic to the individual user, rather than express one choice or the other in the question.
As an aside, zsh
provides a way to do this kind of filtering in the glob itself. You can match only regular files ending with .java
(and disable the default behavior of treating unmatched patterns as an error, rather than as literal text) with
for f in *.java(.N); do
...
done
With the above, you are guaranteed that if you reach the body of the loop, then $f
expands to the name of a regular file. The .
makes *.java
match only regular files, and the N
causes a failed match to expand to nothing instead of producing an error.
There are also other such glob qualifiers for doing all sorts of filtering on filename expansions. (I like to joke that zsh
's glob expansion replaces the need to use find
at all.)
While loop in shell script not working in the linux bash shell
The shebang must be set to the shell that should interpret the script. It doesn't matter which shell you run the script from. The only thing that matters is the language the script is written in.
Your script is written in csh
, and must therefore have the shebang #!/bin/csh
. This is true even if you want to run it from bash. Also, you were missing a space in your at-sign-ment:
$ cat me.csh
#!/bin/csh
set i = 1
echo it starts
while ($i <= 5)
echo i is $i
@ i = $i + 1
end
Output:
$ ./me.csh
it starts
i is 1
i is 2
i is 3
i is 4
i is 5
How can I loop over the output of a shell command?
Never for
loop over the results of a shell command if you want to process it line by line unless you are changing the value of the internal field separator $IFS
to \n
. This is because the lines will get subject of word splitting which leads to the actual results you are seeing. Meaning if you for example have a file like this:
foo bar
hello world
The following for loop
for i in $(cat file); do
echo "$i"
done
gives you:
foo
bar
hello
world
Even if you use IFS='\n'
the lines might still get subject of Filename expansion
I recommend to use while
+ read
instead because read
reads line by line.
Furthermore I would use pgrep
if you are searching for pids belonging to a certain binary. However, since python might appear as different binaries, like python2.7
or python3.4
I suggest to pass -f
to pgrep
which makes it search the whole command line rather than just searching for binaries called python
. But this will also find processes which have been started like cat foo.py
. You have been warned! At the end you can refine the regex passed to pgrep
like you wish.
Example:
pgrep -f python | while read -r pid ; do
echo "$pid"
done
or if you also want the process name:
pgrep -af python | while read -r line ; do
echo "$line"
done
If you want the process name and the pid in separate variables:
pgrep -af python | while read -r pid cmd ; do
echo "pid: $pid, cmd: $cmd"
done
You see, read
offers a flexible and stable way to process the output of a command line-by-line.
Btw, if you prefer your ps .. | grep
command line over pgrep
use the following loop:
ps -ewo pid,etime,cmd | grep python | grep -v grep | grep -v sh \
| while read -r pid etime cmd ; do
echo "$pid $cmd $etime"
done
Note how I changed the order of etime
and cmd
. Thus to be able to read cmd
, which can contain whitespace, into a single variable. This works because read
will break down the line into variables, as many times as you specified variables. The remaining part of the line - possibly including whitespace - will get assigned to the last variable which has been specified in the command line.
Related Topics
Linux: Writes Are Split into 512K Chunks
Best Way Elevate the Privileges Programmatically Under Different Versions of Linux
How to Detect Usb Drive Insertion in Linux
Matlab Mex Socket Wrapper Library
Shell Script for Multithreading a Process
Rename Files and Directories (Add Prefix)
How to Tar Certain File Types in All Subdirectories
How to Detect If a Git Clone Failed in a Bash Script
How to Force Linking with a Static Library When a Shared Library of Same Name Is Present
How to Use Sed to Remove the Last N Lines of a File
Mechanism of Clipboard of Xwindow
If I Have a Process, and I Clone It, Is the Pid the Same
Using Ld_Preload Mixed 64Bit/32Bit Environment in Linux
Dyld_Library_Path Environment Variable Is Not Forwarded to External Command in Makefile on MACos
Copy Differences Between Two Files in Unix
Signals and Interrupts a Comparison
How to Extract Filename.Tar.Gz File
Use Bash to Read a File and Then Execute a Command from the Words Extracted