Dynamically rename an XML File in Shell - linux

I have an XML files named as *dmi_standard_load_B0001_set.xml* and I want to rename it dynamically by accepting a *batch_name* from user and change it to *dmi_standard_load_${batch_name}_set.xml*.
How can I do that ? I tried the following command bt it throws error and doesn't work...
mv dmi_standard_load_*_set.xml dmi_standard_load_${batch_name}_set.xml

So what is your typical batch name any spaces in it. If so you may need to put speech marks around the file name the 2nd bit. Worth echo $ batch_name aldo u could try load_$(echo $ $batch_name)_set.xml

Careful! You are using a * wildcard in your command. If you have multiple XML files in your directory, for instance :
dmi_standard_load_B0001_set.xml
dmi_standard_load_B0002_set.xml
dmi_standard_load_B0003_set.xml
your command will expand to this :
mv dmi_standard_load_B0001_set.xml dmi_standard_load_B0002_set.xml dmi_standard_load_B0003_set.xml dmi_standard_load_${batch_name}_set.xml
and most likely, it will fail, telling you that the last argument is not a directory. So the first thing is, you want to loop through your files, like so :
for f in dmi_standard_load_*_set.xml
do
mv ${f} dmi_standard_load_${batch_name}_set.xml
done
DO NOT RUN THAT CODE THOUGH! We're not finished. The problem here is that you're going to rename each of your XML files... to one and only one file. In effect, you'll end up with a file named dmi_standard_load_${batch_name}_set.xml, with the contents of the last XML file that went through the loop.
You want the user to give a different batch name for each of these files, right? So you need to ask her for each file in your loop, with the read command :
for f in dmi_standard_load_*_set.xml
do
echo Please enter a batch name for file $f :
read batch_name
mv ${f} dmi_standard_load_${batch_name}_set.xml
done
This will work, but there is no error checking... the user could just press the Enter key, giving an empty batch name. Here's a final version that makes sure the user gives a name :
for f in dmi_standard_load_*_set.xml
do
batch_name=
while [[ ${batch_name} == '' ]]
do
echo Please enter a batch name for file $f :
read batch_name
done
mv ${f} dmi_standard_load_${batch_name}_set.xml
done

For example you can use this one-line script:
for file_name in *; do echo -ne "Renaming file \"${file_name}\" Enter batch_name: "; read batch_name; mv "${file_name}" "dmi_standard_load_${batch_name}_set.xml"; done
this is example from my gentoo box:
user#host ~/test $ ls -1
dmi_standard_load_B0001_set.xml
dmi_standard_load_B0002_set.xml
dmi_standard_load_B0003_set.xml
dmi_standard_load_B0004_set.xml
dmi_standard_load_B0005_set.xml
user#host ~/test $ for file_name in *; do echo -ne "Renaming file \"${file_name}\" Enter batch_name: "; read batch_name; mv "${file_name}" "dmi_standard_load_${batch_name}_set.xml"; done
Renaming file "dmi_standard_load_B0001_set.xml" Enter batch_name: test
Renaming file "dmi_standard_load_B0002_set.xml" Enter batch_name: another test
Renaming file "dmi_standard_load_B0003_set.xml" Enter batch_name: and test again
Renaming file "dmi_standard_load_B0004_set.xml" Enter batch_name: four
Renaming file "dmi_standard_load_B0005_set.xml" Enter batch_name: last, five
user#host ~/test $ ls -1
dmi_standard_load_and test again_set.xml
dmi_standard_load_another test_set.xml
dmi_standard_load_four_set.xml
dmi_standard_load_last, five_set.xml
dmi_standard_load_test_set.xml
user#host ~/test $

Related

Bash: "No such file or directory" despite directory existing

I am making a custom command that moves or duplicates a file to a wastebasket directory instead of deleting it. I am trying to make a directory if it already isn't there, make a duplicate if a file has already been executed on, and simply move it if it doesn't. The issue is that I keep getting a no such file or directory error regardless of where I place the wastebasket directory. Do note that simply moving or copying the file with base linux commands work fine, and that being in root doesn't fix the issue. What steps should I take?
#!/bin/bash
set -x
mkdir -p /home/WASTEBASKIT #This makes a wastebasket directory if it doesn't already exist.
if test -e "$1"; then
if test -e /home/WASTEBASKIT/"$1"; then #Checking for duplicate files.
cp "$1" "/home/WASTEBASKIT/$1.$$"
else
mv "$1" "/home/WASTEBASKIT"
fi
else
printf '%s\n' "File not found." #Error if a file is not there.
fi
Here are the results: ++ mkdir -p /home/WASTEBASKIT
++ test -e config.sh
++ test -e /home/WASTEBASKIT/config.sh
++ cp config.sh.945 ' /home/WASTEBASKIT'
cp: cannot stat 'config.sh.945': No such file or directory
cp config.sh.945 ' /home/WASTEBASKIT'
cp: cannot stat 'config.sh.945': No such file or directory
The problem is on this line:
cp "$1" "$1.$$" "/home/WASTEBASKIT"
You try to copy two files into /home/WASTEBASKIT, namely $1 and $1.$$. The latter does not exist.
Change it to:
cp "$1" "/home/WASTEBASKIT/$1.$$"
I suggest that you instead create a unique file since process numbers aren't unique, so instead of the copy above, do something like:
newfile=$(mktemp "WASTEBASKIT/$1.XXXXXXX")
cp -p "$1" "$newfile"
You can then list all the copies with ls -t WASTEBASKIT to get them in historical order, newest first - or with ls -tr WASTEBASKIT to get the oldest first.
Also note: printf'%s\n' "File not found." will likely generate an error like printf%s\n: command not found.... You need to insert a space between the command printf and the argument '%s\n'.
The moving part is also wrong since you have a space before /home. It should be:
mv "$1" /home/WASTEBASKIT
mv "$1" " /home/WASTEBASKIT"
First issue: spaces matter. If you have previously created the /home/WASTEBASKIT directory, and then execute that copy command above, it will not copy the file into that directory - you will most likely end up with a file in your home directory called spaceWASTEBASKIT (unless you already have a directory of that name, including the leading space) in which case it will go into that directory.
Either way, it won't go where you want it to go.
Secondly, the command below is not doing what you seem to think. It will try to copy two files to the directory, the second of which probably does not even exist (config.sh.945 in your case):
cp "$1" "$1.$$" "/home/WASTEBASKIT"
If you want to create a "uniquely" versioned file so as to not overwrite an existing one, that would be:
mv "$1" "/home/WASTEBASKIT/$1.$$"
Note the quotes around the word "uniquely" since there's no guarantee $1.$$ may not also exist in the wastebasket - the PIDs do eventually wrap around at some point, and also do so on reboot.
I suspect a better approach (though still not bullet-proof) would be just to prefix every file with the date and time so that:
you can sort duplicates to establish the order of creation; and
sans date changes, the date/time won't give you possible duplicates (unless you're doing it more then once per second).
That approach would be something like:
mv "$1" "/home/WASTEBASKIT/$(date -u +%Y%m%d_%H%M%S)$1"
or, making duplicates even less likely:
mv "$1" "/home/WASTEBASKIT/$(date -u +%Y%m%d_%H%M%S)_${RANDOM}_$1"

Another Bash permission denied post

I've spent the past hour trying to find a way around this before asking but to no avail so I'm asking.
I am trying to make a simple script that will take the name for a file and then generate a generic blank html template for me.
#!/bin/bash
blank=/home/sithyrys/Documents/scripts/blank.html
echo "Enter file name with no extensions:"
read fileName
fileName+=.html
echo $fileName
touch $fileName
$blank >> $fileName
When I comment out the path the code runs with no error message but then it's not pulling the template and it makes a blank page. The error message in question is:
./basicHTMLTemplate.sh: line 9: /home/sithyrys/Documents/scripts/blank.html: Permission denied
Edit: shebang line copied wrong that was correct already
>> does not copy a file; it appends the output of the command that precedes it to the file named following it. You need to use the cat command to actually "push" the contents of blank.html into the new file.
cat "$blank" >> "$fileName"
As written, your code accommodates the possibility that $fileName already exists and appends the contents of $blank without overwriting the existing file. In practice, it doesn't make much sense to append the template to the end of an existing file, so you probably just want to make a copy of the template.
#!/bin/bash
blank=/home/sithyrys/Documents/scripts/blank.html
echo "Enter file name with no extensions:"
read fileName
fileName+=.html
echo $fileName
cp "$blank" "$fileName"
(or, to guard against overwriting an existing file,
[[ -f "$fileName" ]] || cp "$blank" "$fileName"
)

Copy text from multiple files, same names to different path in bash (linux)

I need help copying content from various files to others (same name and format, different path).
For example, $HOME/initial/baby.desktop has text which I need to write into $HOME/scripts/baby.desktop. This is very simple for a single file, but I have 2500 files in $HOME/initial/ and the same number in $HOME/scripts/ with corresponding names (same names and format). I want append (copy) the content of file in path A to path B (which have the same name and format), to the end of file in path B without erase the content of file in path B.
Example content of $HOME/initial/*.desktop to final $HOME/scripts/*.desktop. I tried the following, but it don't work:
cd $HOME/initial/
for i in $( ls *.desktop ); do egrep "Icon" $i >> $HOME/scripts/$i; done
Firstly, I would backup $HOME/initial and $HOME/scripts, because there is lots of scope for people misunderstanding your question. Like this:
cd $HOME
tar -cvf initial.tar initial
tar -cvf scripts.tar scripts
That will put all the files in $HOME/initial into a single tarfile called initial.tar and all the files in $HOME/scripts into a single tarfile called scripts.tar.
Now for your question... in general, if you want to put the contents of FileB onto the end of FileA, the command is
cat FileB >> FileA
Note the DOUBLE ">>" which means "append" rather than single ">" which means overwrite.
So, I think you want to do this:
cd $HOME/initial/baby.desktop
cat SomeFile >> $HOME/scripts/baby.desktop/SomeFile
where SomeFile is the name of any file you choose to test with. I would test that has worked and then, if you are happy with that, go ahead and run the same command inside a loop:
cd $HOME/initial/baby.desktop
for SOURCE in *
do
DESTINATION="$HOME/scripts/baby.desktop/$SOURCE"
echo Appending "$SOURCE" to "$DESTINATION"
#cat "$SOURCE" >> "$DESTINATION"
done
When the output looks correct, remove the "#" at the start of the penultimate line and run it again.
I solved it, if some people want learn how to resolve is very simple:
using Sed
I need only the match (or pattern) line "Icon=/usr/share/some_picture.png into $HOME/initial/example.desktop to other with same name and format $HOME/scripts/example.desktop, but I had a lot of .desktop files (2500 files)
cd $HOME/initial
STRING_LINE=`grep -l -R "Icon=" *.desktop`
for i in $STRING_LINE; do sed -ne '/Icon=/ p' $i >> $HOME/scripts/$i ; done
_________
If you need only copy all to other file with same name and format
using cat
cd $HOME/initial
STRING_LINE=`grep -l -R "Icon=" *.desktop`
for i in $STRING_LINE; do cat $i >> $HOME/scripts/$i ; done

Move files and rename - one-liner

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.

Removing 10 Characters of Filename in Linux

I just downloaded about 600 files from my server and need to remove the last 11 characters from the filename (not including the extension). I use Ubuntu and I am searching for a command to achieve this.
Some examples are as follows:
aarondyne_kh2_13thstruggle_or_1250556383.mus should be renamed to aarondyne_kh2_13thstruggle_or.mus
aarondyne_kh2_darknessofunknow_1250556659.mp3 should be renamed to aarondyne_kh2_darknessofunknow.mp3
It seems that some duplicates might exist after I do this, but if the command fails to complete and tells me what the duplicates would be, I can always remove those manually.
Try using the rename command. It allows you to rename files based on a regular expression:
The following line should work out for you:
rename 's/_\d+(\.[a-z0-9A-Z]+)$/$1/' *
The following changes will occur:
aarondyne_kh2_13thstruggle_or_1250556383.mus renamed as aarondyne_kh2_13thstruggle_or.mus
aarondyne_kh2_darknessofunknow_1250556659.mp3 renamed as aarondyne_kh2_darknessofunknow.mp3
You can check the actions rename will do via specifying the -n flag, like this:
rename -n 's/_\d+(\.[a-z0-9A-Z]+)$/$1/' *
For more information on how to use rename simply open the manpage via: man rename
Not the prettiest, but very simple:
echo "$filename" | sed -e 's!\(.*\)...........\(\.[^.]*\)!\1\2!'
You'll still need to write the rest of the script, but it's pretty simple.
find . -type f -exec sh -c 'mv {} `echo -n {} | sed -E -e "s/[^/]{10}(\\.[^\\.]+)?$/\\1/"`' ";"
one way to go:
you get a list of your files, one per line (by ls maybe) then:
ls....|awk '{o=$0;sub(/_[^_.]*\./,".",$0);print "mv "o" "$0}'
this will print the mv a b command
e.g.
kent$ echo "aarondyne_kh2_13thstruggle_or_1250556383.mus"|awk '{o=$0;sub(/_[^_.]*\./,".",$0);print "mv "o" "$0}'
mv aarondyne_kh2_13thstruggle_or_1250556383.mus aarondyne_kh2_13thstruggle_or.mus
to execute, just pipe it to |sh
I assume there is no space in your filename.
This script assumes each file has just one extension. It would, for instance, rename "foo.something.mus" to "foo.mus". To keep all extensions, remove one hash mark (#) from the first line of the loop body. It also assumes that the base of each filename has at least 12 character, so that removing 11 doesn't leave you with an empty name.
for f in *; do
ext=${f##*.}
new_f=${base%???????????.$ext}
if [ -f "$new_f" ]; then
echo "Will not rename $f, $new_f already exists" >&2
else
mv "$f" "$new_f"
fi
done

Resources