I have following images.
10.jpg
11.jpg
12.jpg
I want to remove above images. I used following shell script file.
for file in /home/scrapping/imgs/*
do
COUNT=$(expr $COUNT + 1)
STRING="/home/scrapping/imgs/""Img_"$COUNT".jpg"
echo $STRING
mv "$file" "$STRING"
done
So, replaced file name
Img_1.jpg
Img_2.jpg
Img_3.jpg
But, I want to replace the file name like this:
Img_10.jpg
Img_11.jpg
Img_12.jpg
So, How to set COUNT value 10 to get my own output?
The expr syntax is pretty outdated, POSIX shell allows you to do arithmetic evaluation with $(()) syntax. You can just do
#!/usr/bin/env bash
count=10
for file in /home/scrapping/imgs/*; do
[ -f "$file" ] || continue
mv "$file" "/home/scrapping/imgs/Img_$((count++)).jpg"
done
Also from the errors reported in the comments, you seem to be running it from the dash shell. It does not seem to have all the features complying to the standard POSIX shell. Run it with the sh or the bash shell.
And always use lowercase letters for user defined variables in your shell script. Upper case letters are primarily for the environment variables managed by the shell itself.
With rename command you can suffix your files with Img_:
rename 's/^/Img_/' *
The ^ means replace the start of the filename with Img_, i.e: adds a suffix.
Related
Is it possible to execute node.js app from .sh script, return some variable and continue the .sh script?
Something like:
#!/bin/sh
SOME_VARIABLE = node app.js
echo ${SOME_VARIABLE}
Firstly, ensure you're using bash, not sh, as there are significant differences in functionality between the two.
One simple solution is command substitution, although be aware that trailing newlines in the command output will be stripped. Also, when echoing, to protect the contents of the variable (such as spaces and glob characters) from metaprocessing by the shell, you have to double-quote it:
#!/bin/bash
output=$(node app.js);
echo "$output";
Another solution is process substitution in more recent versions of bash. You could even collect the output as an array of lines in this case:
#!/bin/bash
exec 3< <(node app.js);
lines=();
while read -r; do lines+=("$REPLY"); done <&3;
exec 3<&-;
echo "${lines[#]}";
It has been a long time since I did much bash script writing.
This is a bash script to copy and rename files by deleting all before the first period delimiter:
#!/bin/bash
mkdir fullname
mv *.audio fullname
cd fullname
for x in * ;
do
cp $x ../`echo $x | cut -d "." -f 2-`
done
cd ..
ls
It works well for file names with no embedded spaces but not for those with spaces.
How can I change the code to fix this simple Linux bash script? Any suggestions for improving the code for other reasons would also be welcome.
Example filenames, some with embedded spaces and some not (from link)
http://www.homenetvideo.com/demo/index.php?/Radio%20%28VLC%29
Ambient.A6.SOMA Space Station.audio
Blues.B9.Blues Radio U.K.audio
Classical.K3.Radio Stephansdom - Vienna.audio
College.CI.KDVS U of California, Davis.audio
Country.Q1.K-FROG.audio
Easy.G4.WNYU.audio
Eclectic.M2.XPN.audio
Electronica.E2.Rinse.audio
Folk.F1.Radionomy.audio
Hiphop.H1.NPR.audio
Indie.I4.WAUG.audio
Jazz.J6.KCSM.audio
Latin.L3.Mega.audio
Misc.X7.Gaydio.audio
News.N9.KQED.audio
Oldies.O1.Lonestar.audio
OldTime.Y1.Roswell.audio
Progressive.P1.Aural Moon.audio
Rock.R8.WXRT.audio
Scanner.Z3.Montreal.audio
Soul.S1.181.FM.audio
Talk.T2.TWiT.audio
World.W3.Persian.audio
http://lh5.googleusercontent.com/-QjLEiAtT4cw/U98_UFcWvvI/AAAAAAAABv8/gyPhbg8s7Bw/w681-h373-no/homenet-radio.png
Whenever you deal with file names that might have spaces in them, you must reference them as "$x" rather than just $x. That's what's causing your cp command to fail.
Your echo command is also problematic. Although echo does the right thing for simple spaces - it echoes a file named A B C as A B C - it will still fail if you have more than one consecutive space in the name, or whitespace that isn't a simple space character.
Instead of passing the file names to external programs for processing, which always requires getting them through the whitespace-hostile command line, you should use bash built-in functions for string manipulations wherever possible, e.g. ${x%%foo}, ${x#bar} and similar functions. The man page describes them under "Parameter expansion".
Here's my suggestion:
#!/bin/bash
shopt -s nullglob
mkdir fullname
mv *.audio fullname
(
cd fullname || exit
for x in *; do
cp "$x" "../${x#*.}"
done
)
ls
nullglob prevents * from presenting itself if no file matches it. Just optional.
() summons a subshell and saves you from changing back to another directory.
|| exit terminates the subshell if cd fails to change directory.
${x#*.} removes the <first>. from $x and expands it.
I'm new to shell programming. I intend to get directory name after zip file was extracted. The print statement of it is
$test.sh helloworld.zip
helloworld
Let's take a look at test.sh:
#! /bin/sh
length=echo `expr index "$1" .zip`
a=$1
echo $(a:0:length}
However I got the Bad substitution error from the compiler.
And when I mention about 'shell'.I just talking about shell for I don't know the difference between bash or the others.I just using Ubuntu 10.04 and using the terminal. (I am using bash.)
If your shell is a sufficiently recent version of bash, that parameter expansion notation should work.
In many other shells, it will not work, and a bad substitution error is the way the shell says 'You asked for a parameter substitution but it does not make sense to me'.
Also, given the script:
#! /bin/sh
length=echo `expr index "$1" .zip`
a=$1
echo $(a:0:length}
The second line exports variable length with value echo for the command that is generated by running expr index "$1" .zip. It does not assign to length. That should be just:
length=$(expr index "${1:?}" .zip)
where the ${1:?} notation generates an error if $1 is not set (if the script is invoked with no arguments).
The last line should be:
echo ${a:0:$length}
Note that if $1 holds filename.zip, the output of expr index $1 .zip is 2, because the letter i appears at index 2 in filename.zip. If the intention is to get the base name of the file without the .zip extension, then the classic way to do it is:
base=$(basename $1 .zip)
and the more modern way is:
base=${1%.zip}
There is a difference; if the name is /path/to/filename.zip, the classic output is filename and the modern one is /path/to/filename. You can get the classic output with:
base=${1%.zip}
base=${base##*/}
Or, in the classic version, you can get the path with:
base=$(dirname $1)/$(basename $1 .zip)`.)
If the file names can contain spaces, you need to think about using double quotes, especially in the invocations of basename and dirname.
Try running it with bash.
bash test.sh helloworld.zip
-likewise-
"try changing the first line to #!/bin/bash" as comment-answered by – #shellter
Try that in bash :
echo $1
len=$(wc -c <<< "$1")
a="${1}.zip"
echo ${a:0:$len}
Adapt it to fit your needs.
I have two folders full of trainings and corresponding testfiles and I'd like to run the fitting pairs against each other using a shell script.
This is what I have so far:
for x in SpanishLS.train/*.train
do
timbl -f $x -t SpanishLS.test/$x.test
done
This is supposed to take file1(-n).train in one directory, look for file1(-n).test in the other, and run them trough a tool called timbl.
What it does instead is look for a file called SpanishLS.train/file1(-n).train.test which of course doesn't exist.
What I tried to do, to no avail, is truncate $x in a way that lets the script find the correct file, but whenever I do this, $x is truncated way too early, resulting in the script not even finding the .train file.
How should I code this?
If I got you right, this will do the job:
for x in SpanishLS.train/*.train
do
y=${x##*/} # strip basepath
y=${y%.*} # strip extention
timbl -f $x -t SpanishLS.test/$y.test
done
Use basename:
for x in SpanishLS.train/*.train
do
timbl -f $x -t SpanishLS.test/$(basename "$x" .train).test
done
That removes the directory prefix and the .train suffix from $x, and builds up the name you want.
In bash (and other POSIX-compliant shells), you can do the basename operation with two shell parameter expansions without invoking an external program. (I don't think there's a way to combine the two expansions into one.)
for x in SpanishLS.train/*.train
do
y=${x##*/} # Remove path prefix
timbl -f $x -t SpanishLS.test/${y%.train}.test # Remove .train suffix
done
Beware: bash supports quite a number of (useful) expansions that are not defined by POSIX. For example, ${y//.train/.test} is a bash-only notation (or bash and compatible shells notation).
Replace all occurences of .train in the filename to .text:
timbl -f $x -t $(echo $x | sed 's/\.train/.text/g')
I've got the below simple script that calls an external script with a number of filenames and arguments of either a delimiter or a set of cut positions. My question: is there a way to make the filename 'dynamic using wildcards' in the sense that the directory will always contain those filenames but with extra text on either end? But the script can do some sort of match up to get the full filename based on a 'contains'.
current /release/ext/ directory contents:
2011storesblah.dat
hrlatest.dat
emp_new12.txt
ie the directory contains these files today (but next week the filenames in this directory could have a slightly different prefix.
eg:
stores_newer.dat
finandhr.dat
emps.txt
Script:
#!/bin/bash
FILES='/release/ext/stores.dat "|"
/release/ext/emp.txt 1-3 4-11 15-40
/release/ext/hr.dat "|" 2'
for f in $FILES
do
echo `sh myexternalscript.sh $f`;
done
Note: there is no need to handle a scenario where the file in my script matches more than 2 files in the direc (it will always only match one).
Also it only can match the file types that are specified in the script.
Also, I don't need to search recursively, just needs to look in the /release/ext/ directory only.
I'm running SunOS 5.10.
$FILES=`find /release/ext -name *stores*.dat`
for FILE in $FILES do
# need to test for empty, case $FILES is empty
test -n "$FILE" && /do/whatever/you/want
done;
It is unclear what the pipe characters and numbers are for in your $FILES variable. However, here is something you might find useful:
#!/bin/bash
filespecs='*stores*.dat *hr*.dat *emp*.txt'
dir='/release/ext'
cd "$dir"
for file in $filespecs
do
sh myexternalscript.sh "$dir/$file"
done
Note that your question is tagged "bash" and you use "bash" in your shebang, but for some reason, you use "sh" when you call your other script. On some systems, sh is symlinked to Bash, but it will behave differently than Bash when called directly. On many systems, sh is completely separate from Bash.
In order to expand the globs and incorporate other arguments, you will need to violate the Bash rule of always quoting variables (this is an example of one of the exceptions).
filespecs='*stores*.dat | 3
*hr*.dat 4 5
*emp*.txt 6 7 8'
while read -r spec arg1 arg2 arg3 arg4
do
sh myexternalscript.sh "$dir"/$spec "$arg1" "$arg2" "$arg3" "$arg4"
done < <(echo "$filespecs")
Use as many "arg" arguments as you think you'll need. Extras will be passed as empty, but set arguments. If there are more arguments than variables to accept them, then the last variable will contain all the remainders in addition to the one that corresponds to it. This version doesn't need the cd since the glob isn't expanded until the directory has been prepended, while in the first version the glob is expanded before the directory is prepended.
If you quote the pipes in the manner shown in your question, then the double quotes will be included in the argument. In the way I show it, only the pipe character gets passed but it's protected since the variable is quoted at the time it's referenced.