Run Command on Multiple of Files or Single File - bash

I needed to convert several pnm image files to jpeg with pnmtojpeg. So I used this script, which I named 'pnm2jpg':
for f in *.pnm;
do pnmtojpeg -quality=85 "$f" > "${f%.pnm}.jpg";
done
This works very nicely. However, I would like to adapt it further so that it can be used for a single file as well.
In other words, if no files are specified in the command line, then process all the files.
$ pnm2jpg thisfile.pnm # Process only this file.
$ pnm2jpg # Process all pnm files in the current directory.
Your insight is greatly appreciated- Thank you.

Something like:
#!/bin/bash
if [[ -z "$1" ]]; then
for f in *.pnm; do
pnmtojpeg -quality=85 "$f" > "${f%.pnm}.jpg"
done
else
pnmtojpeg -quality=85 "$1" > "${1%.pnm}.jpg"
fi
If you execute pnm2jpg without an argument the if block is processed.
if you execute pnm2jpg thisfile.pnm the else block is processed.

Related

Replace file only if not being accessed in bash

My requirement is to replace file only when it is not being accessed. I have following snippet:
if [ -f file ]
then
while true
do
if [ -n "$(fuser "file")" ]
then
echo "file is in use..."
else
echo "file is free..."
break
fi
done
fi
{
flock -x 3
mv newfile file
} 3>file
But I have a doubt that I am not handling concurrency properly. Please give some insights and possible way to achieve this.
Thanks.
My requirement is to replace file only when it is not being accessed.
Getting requirements right can be hard. In case your actual requirement is the following, you can boil down the whole script to just one command.
My guess on the actual requirement (not as strict as the original):
Replace file without disturbing any programs reading/writing file.
If this is the case, you can use a very neat behavior: In Unix-like systems file descriptors always point to the file (not path) for which they where opened. You can move or even delete the corresponding path. See also How do the UNIX commands mv and rm work with open files?.
Example:
Open a terminal and enter
i=1; while true; do echo $((i++)); sleep 1; done > file &
tail -f file
The first command writes output to file and runs in the background. The second command reads the file and continues to print its changing content.
Open another terminal and move or delete file, for instance with
mv file file2
echo overwritten > otherFile
mv otherFile file2
rm file2
echo overwritten > file
echo overwritten > file2
While executing these commands have a look at the output of tail -f in the first terminal – it won't be affected by any of these commands. You will never see overwritten.
Solution For New Requirement:
Because of this behavior you can replace the whole script with just one mv command:
mv newfile file
Consider lsof.
mvWhenClear() {
while [[ -f "$1" ]] && lsof "$1"
do sleep $delay
done
mv "$1" "$2" # still allows race condition
}

loop over files with bash script

I have a js script that converts kml location history files to csv. I wrote a bash script to loop through all the files in a directory. The script works when I execute it from command line ./index.js filename.kml > filename.csv
But nothing happens when I execute the bash file that is supposed to loop through all files.
I know it probably is a simple mistake but I can't spot it.
#!/bin/bash
# file: foo.sh
for f in *.kml; do
test -e "${f%.kml}" && continue
./index.js "$f" > "-fcsv"
done
Just delete the "&& continue", if I'm not wrong you're skipping the current iteration with the "continue" keyword, that's why nothing happens
EDIT
Also, you shouldn't test if the file exists, the for loop is enough to be sure that "f" will be a valid .kml file. Anyways, if you still want to do it you have to do it like:
#!/bin/bash
# file: foo.sh
for f in *.kml; do
if [ -e "$f" ]; then
./index.js "$f" > "$f.csv"
fi;
done

Shell Script to write to a file upto a certain point and then keep overwriting the file

I am trying to write a shell script , which will write the output of another script in a file and it will keep writing to that upto a certain point and then it will overwrite the file so that file size will remain within a well bounded range.
while true
do
./runscript.sh > test.txt
sleep 1
done
I have tried to use infinite loop and sleep so that it will keep overwrite that file.
But, it shows a different behaviour. Till the point command is running , the filesize keeps on increasing. But, when i stop the command, the file size get reduce.
How can i keep overwriting the same file and maintain the file size along with it.
use truncate -s <size> <file> to shrink the file when its size is out of your boundary
I will do with below script
#!/bin/sh
Logfile=test.txt
minimumsize=100000 # define the size you want
actualsize=$(wc -c <"$Logfile")
if [[ $actualsize -ge $minimumsize ]]; then
rm -rf "$Logfile"
sh ./runscript.sh >> test.txt
else
#current_date_time="`date +%Y%m%d%H%M%S`"; #add this to runscript.sh to track when it was written
#echo "********Added at :$current_date_time ********" #add this to runscript.sh to track when it was written
sh ./runscript.sh >> test.txt
fi
I can try with the option for generating the new file once the old one
is full. … How can make the
script to generate the new file and write to it.
The following script, let's call it chop.sh, does that; you use it by feeding the output to it, specifying the desired file size and name as arguments, e. g. ./runscript.sh|chop.sh 999999 test.txt.
File=${2?usage: $0 Size File}
Size=$1
while
set -- `ls -l "$File" 2>/dev/null` # 5th column is file size
[ "$5" -lt "$Size" ] || mv "$File" "$File"-old
read -r && echo "$REPLY" >>"$File"
do :
done
The old (full) file would then be named test.txt-old.

executing a command on multiple paired files

Say I have a command, command.py, and it pairs together files, File_01_R1.fastq to File_01_R2.fastq. The command executed on a single pair looks like this:
command.py -f File_01_R1.fastq -r File_01_R2.fastq
I have many files however, each with a R1 and R2 version. How can I tell this command to go through every file I have, so it also executes
command.py -f File_02_R1.fastq -r File_02_R2.fastq
command.py -f File_03_R1.fastq -r File_03_R2.fastq
and so on.
You may use a simple parameter expansion:
for f in *_R1.fastq; do
echo command.py -f "$f" -r "${f%_R1.fastq}_R2.fastq"
done
This will just print out what's to be executed. Remove the echo if you're happy with the result.
# Loop over all R1.fastq files
for f in File_*_R1.fastq; do
# Replace R1 with R2 in the filename and run the command on both files.
command.py -f "$f" -r "${f/_R1./_R2.}"
done; unset -v f
As #gniourf_gniourf indicates in his comment my answer is slightly less safe than his in that it may match at an incorrect location in the filename (whereas his is anchored at the end).

Passing a path as an argument to a shell script

I've written bash script to open a file passed as an argument and write it into another file. But my script will work properly only if the file is in the current directory. Now I need to open and write the file that is not in the current directory also.
If compile is the name of my script, then ./compile next/123/file.txt should open the file.txt in the passed path. How can I do it?
#!/bin/sh
#FIRST SCRIPT
clear
echo "-----STARTING COMPILATION-----"
#echo $1
name=$1 # Copy the filename to name
find . -iname $name -maxdepth 1 -exec cp {} $name \;
new_file="tempwithfile.adb"
cp $name $new_file #copy the file to new_file
echo "compiling"
dir >filelist.txt
gcc writefile.c
run_file="run_file.txt"
echo $name > $run_file
./a.out
echo ""
echo "cleaning"
echo ""
make clean
make -f makefile
./semantizer -da <withfile.adb
Your code and your question are a bit messy and unclear.
It seems that you intended to find your file, given as a parameter to your script, but failed due to the maxdepth.
If you are given next/123/file.txt as an argument, your find gives you a warning:
find: warning: you have specified the -maxdepth option after a
non-option argument -iname, but options are not positional (-maxdepth
affects tests specified before it as well as those specified after
it). Please specify options before other arguments.
Also -maxdepth gives you the depth find will go to find your file until it quits. next/123/file.txt has a depth of 2 directories.
Also you are trying to copy the given file within find, but also copied it using cp afterwards.
As said, your code is really messy and I don't know what you are trying to do. I will gladly help, if you could elaborate :).
There are some questions that are open:
Why do you have to find the file, if you already know its path? Do you always have the whole path given as an argument? Or only part of the path? Only the basename ?
Do you simply want to copy a file to another location?
What does your writefile.c do? Does it write the content of your file to another? cp does that already.
I also recommend using variables with CAPITALIZED letters and checking the exit status of used commands like cp and find, to check if these failed.
Anyway, here is my script that might help you:
#!/bin/sh
#FIRST SCRIPT
clear
echo "-----STARTING COMPILATION-----"
echo "FILE: $1"
[ $# -ne 1 ] && echo "Usage: $0 <file>" 1>&2 && exit 1
FILE="$1" # Copy the filename to name
FILE_NEW="tempwithfile.adb"
cp "$FILE" "$FILE_NEW" # Copy the file to new_file
[ $? -ne 0 ] && exit 2
echo
echo "----[ COMPILING ]----"
echo
dir &> filelist.txt # list directory contents and write to filelist.txt
gcc writefile.c # ???
FILE_RUN="run_file.txt"
echo "$FILE" > "$FILE_RUN"
./a.out
echo
echo "----[ CLEANING ]----"
echo
make clean
make -f makefile
./semantizer -da < withfile.adb

Resources