I need to add the word "hallo" at the end of each filename before extension in the given directory. but my code produces no output. the echo statements give no output and the file names are not changed
count=""
dot="."
for file in $ls
do
echo $file
count=""
for f in $file
do
echo $f
if [ $f -eq $dot ];
then
count=$count"hallo"
fi
count=$count+$f
done
mv $file $count
done
@GeorgeVasiliou has shown how to do what you want; I'll take a stab at explaining what's wrong with your original script. There are a number of places where you're using the wrong syntax for what you're trying to do.
This will expand a shell variable named
ls
, split its contents into words, expand any wildcards found in those words into lists of matching files, and then iterate over the result. But there's nols
variable defined, so it expands to nothing, and the loop never runs. I think you're trying to iterate over the output of thels
command; the syntax for that isfor file in $(ls)
. But you shouldn't do that, because filenames can contain spaces and tabs and linefeeds and wildcards and all manner of other funny characters that can make this iterate over... something other than a list of filenames. See the BashFAQ entry 'Why you shouldn't parse the output of ls(1)'.What you should use instead is
for file in *
. The*
gets expanded to a list of matching files (all visible files in the current directory), and then that list is not messed with in any way, just iterated over directly. The only potential problem is that if there aren't any matches, expansion gets skipped, and the loop runs once withfile
set to "*".When a variable is used without double-quotes around it, it'll undergo that word splitting and wildcard expansion process I described above. Usually, nothing happens. Sometimes, weird and unexpected things happen. Use double-quotes, like
echo "$file"
.The
for ... in ...
statement iterates over words, not characters. The shell doesn't have a particularly good way to iterate over the characters in a string, but you can iterate over the character numbers withfor ((i=0; i<${#file}; i++)); do
and then get the individual characters with"${file:$i:1}"
.Oh, and the
for (( ))
and${var:start:length}
constructs are bash extensions that aren't available in all shells. If you want to use either or both in your script, be sure to start it with a bash-specific shebang line (#!/bin/bash
or#!/usr/bin/env bash
).Again, double-quote variable references to avoid chaos when the variables contain spaces or wildcards, or are blank. Also, in a
[ ]
test, the-eq
operator does numeric comparison not string comparison; if you give it strings that aren't valid numbers, it'll give an error. For string comparison, use=
instead. Also, a semicolon isn't needed at the end of the line (well, except for a few places like the end of acase
). You'd use a semicolon here if thethen
was on the same line, but not if it's on the next line.The
+
here will be inserted as a literal character in the variable value. To append two variables, just use something likecount="$count$f"
. (Note: this is actually one of the few places where it's safe to leave the double-quotes off. But I recommend using them anyway, because it's hard to keep track of where it's safe and where it isn't, so it's much easier to just always double-quote your variables.)Again, double-quotes. Also, when running any sort of automated bulk rename/move like this, you should always use
mv -n
ormv -i
to keep it from overwriting files if a naming conflict (or bug or whatever) occurs.Actually, when I'm running a mass-mv script like this for the first time, I like to add
echo
before the active command (e.g.echo mv -n "$file" "$count"
) so I can do a dry run and make sure it's going to do what I expect, before I remove theecho
and run it for real.With bash this can be done simplier like:
example:
This works because
${f%.*}
returns filename without extension - deletes everything (*) from the end (backwards) up to first/shortest found dot.On the other hand this one
${f##*.}
deletes everything from the beginning up to the longest found dot, returning only the extension.To overcome the extensionless files problem as pointed out in comments you can do something like this:
If the file has not an extension then this will yeld true
"${f%.*}" == "${f}"