I've got the following code:
cd /home/pi/.attract/romlists; for file in *.tag ; do grep -v -F 'Arcade Snooker [AGA]' $file > $file1.tmp && mv -f $file1.tmp $file; done
This works well to remove the fixed string "Arcade Snooker [AGA]" from a file such as "Amiga.tag". However, it does not remove the string from "Atari 800.tag" or from any other file that has a "space" in its name. The result for "Atari 800.tag" is the following error:
grep: Atari: No such file or directory
grep: 800.tag: No such file or directory
What changes do I need to make to the code please to get it to remove "Arcade Snooker [AGA]" from "Atari 800.tag" and from other such files containing spaces in their names?
I would prefer to have one line of code only please as I have it now.
Thanks.
You need to quote the variables if the file names contain spaces...
cd /home/pi/.attract/romlists; for file in *.tag ; do grep -v -F 'Arcade Snooker [AGA]' "$file" > "$file1.tmp" && mv -f "$file1.tmp" "$file"; done
Related
I am new to bash loops and trying to rename all files in a directory to their appropriate md5 values.
There are 5 sample files in the directory.
For testing purpose, I am trying to first just print md5 hashes of all files in the directory using below command and it is working fine.
for i in `ls`; do md5sum $i; done
Output:
edc47be8af3a7d4d55402ebae9f04f0a file1
72cf1321d5f3d2e9e1be8abd971f42f5 file2
4b7b590d6d522f6da7e3a9d12d622a07 file3
357af1e7f8141581361ac5d39efa4d89 file4
1445c4c1fb27abd9061ada3b30a18b44 file5
Now I am trying to rename each file with its appropriate md5 hashes by following command:
for i in `ls`; do mv $i md5sum $i; done
Failed Output:
mv: target 'file1' is not a directory
mv: target 'file2' is not a directory
mv: target 'file3' is not a directory
mv: target 'file4' is not a directory
mv: target 'file5' is not a directory
What am I missing here?
Your command is expanded to
mv file1 edc47be8af3a7d4d55402ebae9f04f0a file1
When mv has more than two non-option arguments, it understands the last argument to be the target directory to which all the preceding files should be moved. But there's no directory file1.
You can use parameter expansion to remove the filename from the string.
Parameter expansion is usually faster then running an external command like cut or sed, but if you aren't renaming thousands of files, it probably doesn't matter.
for f in *; do
m=$(md5sum "$f")
mv "$f" ${m%% *} # Remove everything after the first space
done
Also note that I don't parse the output of ls, but let the shell expand the glob. It's safer and works (with proper quoting) for filenames containing whitespace.
Syntax. Yes I was giving wrong syntax.
With some trial and errors with the command, I finally came up with the correct syntax.
I noticed that md5sum $i was giving me 2 column-ed output.
edc47be8af3a7d4d55402ebae9f04f0a file1
72cf1321d5f3d2e9e1be8abd971f42f5 file2
4b7b590d6d522f6da7e3a9d12d622a07 file3
357af1e7f8141581361ac5d39efa4d89 file4
1445c4c1fb27abd9061ada3b30a18b44 file5
By firing second command for i in ls; do mv $i md5sum $i; done, I was basically telling terminal to do something like :
mv $i md5sum $i
which, upto my knowledge, turns out to be
mv file1 <md5 value> file1 <-- this was the issue.
How I resolved the issue?
I used cut command to filter out required value and made new one-liner as below:
for i in `ls`; do mv $i "$(md5sum $i | cut -d " " -f 1)"; done
[Edit]
According to another answer and comment by #stark, #choroba and #tripleee, it is better to use * instead of ls.
for i in *; do mv $i "$(md5sum $i | cut -d " " -f 1)"; done
#choroba's answer is also a good addition here. Turning it into one-liner requirement, below is his solution:
for i in *; do m=$(md5sum $i); mv "$i" ${m%% *};done
I am currently trying to list the size of all files in a directory which is passed as the first argument to the script, but the -f option in Linux is not working, or am I missing something.
Here is the code :
for tmp in "$1/*"
do
echo $tmp
if [ -f "$tmp" ]
then num=`ls -l $tmp | cut -d " " -f5`
echo $num
fi
done
How would I fix this problem?
I think the error is with your glob syntax which doesn't work in either single- or double-quotes,
for tmp in "$1"/*; do
..
Do the above to expand the glob outside the quotes.
There are couple more improvements possible in your script,
Double-quote your variables to prevent from word-splitting, e.g. echo "$temp"
Backtick command substitution `` is legacy syntax with several issues, use the $(..) syntax.
The [-f "filename"] condition check in linux is for checking the existence of a file and it is a regular file. For reference, use this text as reference,
-b FILE
FILE exists and is block special
-c FILE
FILE exists and is character special
-d FILE
FILE exists and is a directory
-e FILE
FILE exists
-f FILE
FILE exists and is a regular file
-g FILE
FILE exists and is set-group-ID
-G FILE
FILE exists and is owned by the effective group ID
I suggest you try with [-e "filename"] and see if it works.
Cheers!
At least on the command line, this piece of script does it:
for tmp in *; do echo $tmp; if [ -f $tmp ]; then num=$(ls -l $tmp | sed -e 's/ */ /g' | cut -d ' ' -f5); echo $num; fi; done;
If cut uses space as delimiter, it cuts at every space sign. Sometimes you have more than one space between columns and the count can easily go wrong. I'm guessing that in your case you just happened to echo a space, which looks like nothing. With the sed command I remove extra spaces.
I'm trying to concatenate a bunch of files into a string so I can use them for a function.
As a test script I'm trying to do this:
#!/bin/bash
for line in $(cat list.txt)
do
x=" "
A=$A$line$x
done
echo "$A"
mv "$A" ./stuff
but I'm getting the error:
mv: cannot stat ‘x.dat y.dat z.dat ’: No such file or directory
but they are most definitely there
can I get some advice please?
This solution will handle file names with spaces too.
#!/bin/bash
mapfile -t lines < list.txt
echo "${lines[#]}"
mv "${lines[#]}" ./stuff/
It reads the entire contents of the file into an array variable, displays the content of the entire array, and finally uses those values in the mv command
Change the last line to mv $A ./stuff
That should work with files that do not have space in their names.
I am trying to edit a script I found online. The script is supposed to convert all files in a directory and place them in another directory (Using handbrake) but it only passes the first word of the file and not the subsequent words or the file extension
#!/bin/bash
SRC="/var/www/mediacenter/convert"
DEST="/var/www/mediacenter/converted"
DEST_EXT=mp4
HANDBRAKE_CLI=HandBrakeCLI
#PRESET = " -e x264 -E av_aac -q 22"
for FILE in "$SRC"/*
do
filename=$(echo $FILE | cut -f 1 -d '.')
extension="${filename##*.}"
filename="${filename%.*}"
echo $filename.$DEST_EXT
$HANDBRAKE_CLI -i "$FILE" -o $FILE.$DEST_EXT $PRESET
done
The echo works and I have tried quoting it but can't find the right combination.
You need to add double quotes when a file name contains whitespace.
$HANDBRAKE_CLI -i "$FILE" -o "$FILE.$DEST_EXT" $PRESET
I'm encountering many files with the same content and the same name on some of my servers. I need to quarantine these files for analysis so I can't just remove the duplicates. The OS is Linux (centos and ubuntu).
I enumerate the file names and locations and put them into a text file.
Then I do a for statement to move the files to quarantine.
for file in $(cat bad-stuff.txt); do mv $file /quarantine ;done
The problem is that they have the same file name and I just need to add something unique to the filename to get it to save properly. I'm sure it's something simple but I'm not good with regex. Thanks for the help.
Since you're using Linux, you can take advantage of GNU mv's --backup.
while read -r file
do
mv --backup=numbered "$file" "/quarantine"
done < "bad-stuff.txt"
Here's an example that shows how it works:
$ cat bad-stuff.txt
./c/foo
./d/foo
./a/foo
./b/foo
$ while read -r file; do mv --backup=numbered "$file" "./quarantine"; done < "bad-stuff.txt"
$ ls quarantine/
foo foo.~1~ foo.~2~ foo.~3~
$
I'd use this
for file in $(cat bad-stuff.txt); do mv $file /quarantine/$file.`date -u +%s%N`; done
You'll get everyfile with a timestamp appended (in nanoseconds).
You can create a new file name composed by the directory and the filename. Thus you can add one more argument in your original code:
for ...; do mv $file /quarantine/$(echo $file | sed 's:/:_:g') ; done
Please note that you should replace the _ with a proper character which is special enough.