Renaming an image with a number inside a text file - shell

I have a folder containing images and textfiles.
The image name is in this order: tramnummerx.JPG
The text file is in this order : tramnummerx.txt
to be clear: x is a variable number
Inside the .txt is a 4 digit number, for example 6303.
In this case I would like to rename the .JPG file to : tramnummerx-6303.JPG
In my current script I did this:
x=O
extention='*.txt'
for i in 'ls $extention'
do
x = 'expr $x + 1'
y= ??? the command for extracting the number ???
mv tramnummerx.JPG tramnummerx-$y.JPG
done
I tried some things like awk '{print $(NF-1), $NF;}' filename for "y=" but I just ended up deleting my files this way.
Thanks in advance!
SOLUTION:
for i in 'ls $extention'
do
x='expr $x +1'
read y < tramnummer$x.txt
mv tramnummer$x.JPG tramnummer$x-$y.JPG
done

Use regular expression matching to extract the number from the image file name, and read to extract the new number from the corresponding text file. (Some details may need adjusting if I misunderstood your question, but the general approach will stay the same.)
for img in *.JPG; do
[[ $img =~ tramnummer([[:digit:]]+).JPG ]]
x=${BASH_REMATCH[1]}
read y < tramnummer$x.txt
mv tramnummer$x.JPG tramnummer$x-$y.JPG
done
In your existing code, you could use lines like
x=$(( x + 1 ))
y=$(< tramnummer$x.txt )
but the proposed solution is a little cleaner and more idiomatic.

Related

Bash Bulk Rename Folders with 3-Digit Prefix and Delimiter

I have a series of folders that I'd like to rename with a prefix number and delimited text. For instance:
% ls
blue green keyboard pictures red tango yellow
flyer gum orange pop runner videos
rename to:
% ls
001-blue 002-green 003-keyboard 004-pictures 005-red 006-tango 007-yellow
008-flyer 009-gum 010-orange 011-pop 012-runner 013-videos
I am using the following to rename except that after 009, I then have 0010, 0011, and so on. I would like to keep prefix numbers to 3 digits.
% i=0; for x in *; do; mv "$x" "00$i-$x" ; i=$((i + 1)); done
I know the problem is in the mv command because of the hard-coded 00 in the destination name, but I don't know how to change that to a 3-digit exclusive destination name with the $i variable.
Thanks in advance.
Use this Perl one-liner:
perl -le '$cmd = sprintf( "mv $_ %03d-$_", ++$i ) and system $cmd for #ARGV;'
To do a dry run and print the intended commands without renaming any files, use print instead of system, like so:
perl -le '$cmd = sprintf( "mv $_ %03d-$_", ++$i ) and print $cmd for #ARGV;'
The Perl one-liner uses these command line flags:
-e : Tells Perl to look for code in-line, instead of in a file.
-l : Strip the input line separator ("\n" on *NIX by default) before executing the code in-line, and append it when printing.
See also the docs for sprintf.

How can I create a Bash script that creates multiple files with text, excluding one?

I need to create Bash script that generates text files named file001.txt through file050.txt
Of those files, all should have this text inserted "This if file number xxx" (where xxx is the assigned file number), except for file007.txt, which needs to me empty.
This is what I have so far..
#!/bin/bash
touch {001..050}.txt
for f in {001..050}
do
echo This is file number > "$f.txt"
done
Not sure where to go from here. Any help would be very appreciated.
#!/bin/bash
for f in {001..050}
do
if [[ ${f} == "007" ]]
then
# creates empty file
touch "${f}.txt"
else
# creates + inserts text into file
echo "some text/file" > "${f}.txt"
fi
done
The continue statement can be used to skip an iteration of a loop and go on to the next -- though since you actually do want to take an operation on file 7 (creating it), it makes just as much sense to have a conditional:
for (( i=1; i<50; i++ )); do
printf -v filename '%03d.txt' "$i"
if (( i == 7 )); then
# create file if it doesn't exist, truncate if it does
>"$filename"
else
echo "This is file number $i" >"$filename"
fi
done
A few words about the specific implementation decisions here:
Using touch file is much slower than > file (since it starts an external command), and doesn't truncate (so if the file already exists it will retain its contents); your textual description of the problem indicates that you want 007.txt to be empty, making truncation appropriate.
Using a C-style for loop, ie. for ((i=0; i<50; i++)), means you can use a variable for the maximum number; ie. for ((i=0; i<max; i++)). You can't do {001..$max}, by contrast. However, this does need meaning to add zero-padding in a separate step -- hence the printf.
Of course, you can costumize the files' name and the text, the key thing is the ${i}. I tried to be clear, but let us know if you don't understand something.
#!/bin/bash
# Looping through 001 to 050
for i in {001..050}
do
if [ ${i} == 007 ]
then
# Create an empty file if the "i" is 007
echo > "file${i}.txt"
else
# Else create a file ("file012.txt" for example)
# with the text "This is file number 012"
echo "This is file number ${i}" > "file${i}.txt"
fi
done

Remove partial duplicates from text file

My bash-foo is a little rusty right now so I wanted to see if there's a clever way to remove partial duplicates from a file. I have a bunch of files containing thousands of lines with the following format:
String1|String2|String3|String4|String5|String6|...|String22|09-Apr-2016 05:28:03|x
Essentially it's a bunch of pipe delimited strings, with the final two columns being a timestamp and x. What I'd like to do is concatenate all of my files and then remove all partial duplicates. I'm defining partial duplicate as a line in the file that matches from String1 up to String22, but the timestamp can be different.
For example, a file containing:
String1|String2|String3|String4|String5|String6|...|String22|09-Apr-2016 05:28:03|x
String1|String2|String3|String4|String5|String6|...|String22|09-Apr-2016 12:12:12|x
String124|String2|String3|String4|String5|String6|...|String22|09-Apr-2016 05:28:03|x
would become:
String1|String2|String3|String4|String5|String6|...|String22|09-Apr-2016 05:28:03|x
String124|String2|String3|String4|String5|String6|...|String22|09-Apr-2016 05:28:03|x
(It doesn't matter which timestamp is chosen).
Any ideas?
Using awk you can do this:
awk '{k=$0; gsub(/(\|[^|]*){2}$/, "", k)} !seen[k]++' file
String1|String2|String3|String4|String5|String6|String7|09-Apr-2016 05:28:03|x
String124|String2|String3|String4|String5|String6|String7|09-Apr-2016 05:28:03|x
awk command first makes a variable k by removing last 2 fields from each line. Then it uses an associative array seen with key as k where it prints only first instance of key by storing each processes key in the array.
If you have Bash version 4, which supports associative arrays, it can be done fairly efficiently in pure Bash:
declare -A found
while IFS= read -r line || [[ -n $line ]] ; do
strings=${line%|*|*}
if (( ! ${found[$strings]-0} )) ; then
printf '%s\n' "$line"
found[$strings]=1
fi
done < "$file"
same idea with #anubhava, but I think more idiomatic
$ awk -F'|' '{line=$0;$NF=$(NF-1)=""} !a[$0]++{print line}' file
String1|String2|String3|String4|String5|String6|...|String22|09-Apr-2016 05:28:03|x
String124|String2|String3|String4|String5|String6|...|String22|09-Apr-2016 05:28:03|x

change a single word in a file with Bash

i try to write a very simple bash file that allow my to open and modify n times a file.java .
The modification i want is only a change in a single (or two) row of a single number.
I try to do this with the follow code:
#!/bin/bash
# commento
touch ic.java
touch input
n=0
for n in "1" "2" "3" "4.5"
do
echo 'import java.io.*;'>ic.java
echo 'import java.util.*;'>>ic.java
echo ' '>>ic.java
echo 'class INITIAL_CONDITION_NORMAL {'>>ic.java
echo 'public static void main (String args[]) {'>>ic.java
echo "$n">>ic.java
n=$(($n+1))
echo '....'>>ic.java
done
java ic.java
as you see i must write all the file and, when i like to change the number, put the "$n"
and n=$(($n+1)) in the row then go on until the end of the file and lounch it (java ic.java).
I know i can use something like:
sed -i 'm-th_row/old/new/' ic.java
but if i want to do this recursively (100 times) whit every time a different new value (as in the example) how can i do that?
Thanks a lot for Your help !
As long as new contains no / (slash) character, or any other special character that would confuse sed, this is the sort of pattern you need.
for n in "1" "2" "3" "4.5"
sed -i "m-th_row/old/$n/" ic.java
done
Of course, that snippet would just modify the same file repeatedly, which probably wouldn't be helpful, but you get the idea.

rename a set of files, by changing their prefix

I've got a set of four directories
English.lproj
German.lproj
French.lproj
Italian.lprj
each of this contains a serie of XMLs named
2_symbol.xml
4_symbol.xml
5_symbol.xml
... and so on ...
I need to rename all of these files into another numerical pattern,
because the code that determined those numbers have changed.
so the new numerical pattern would be like
1_symbol.xml
5_symnol.xml
3_symbol.xml
... and so on ...
so there's no algorithm applicable to determine this serie, because of this
reason I thought about storing the two numerical series into an array.
I was thinking to a quick way of doing it with a simple bash script.
I think that I'd need an array to store the old numerical pattern and another
array to store the new numerical pattern, so that I can perform a cycle to make
# move n_symbol.xml newdir/newval_symbol.xml
any suggestion?
thx n cheers.
-k-
you don't need bash for this, any POSIX-compatible shell will do.
repls="1:4 2:1 4:12 5:3"
for pair in $repls; do
old=${pair%:*}
new=${pair#*:}
file=${old}_symbol.xml
mv $file $new${file#$old}
done
edit: you need to take care of overwriting files. the snippet above clobbers 4_symbol.xml, for example.
for pair in $repls; do
...
mv $file $new${file#$old}.tmp
done
for f in *.tmp; do
mv $f ${f%.tmp}
done
The following script will randomly shuffle the symbol names of all xml files across 'lproj' directories.
#!/bin/bash
shuffle() { # Taken from http://mywiki.wooledge.org/BashFAQ/026
local i tmp size max rand
size=${#array[*]}
max=$(( 32768 / size * size ))
for ((i=size-1; i>0; i--)); do
while (( (rand=$RANDOM) >= max )); do :; done
rand=$(( rand % (i+1) ))
tmp=${array[i]} array[i]=${array[rand]} array[rand]=$tmp
done
}
for file in *lproj/*.xml; do # get an array of symbol names
tmp=${file##*/}
array[$((i++))]=${tmp%%_*}
done
shuffle # shuffle the symbol name array
i=0
for file in *lproj/*.xml; do # rename the files with random symbols
echo mv "$file" "${file%%/*}/${array[$((i++))]}_${file##*_}"
done
Note: Remove the echo in front of the mv when you are satisfied with the results and re-run the script to make the changes permanent.
Script Output
$ ./randomize.sh
mv 1.lproj/1_symbol.xml 1.lproj/16_symbol.xml
mv 1.lproj/2_symbol.xml 1.lproj/12_symbol.xml
mv 1.lproj/3_symbol.xml 1.lproj/6_symbol.xml
mv 1.lproj/4_symbol.xml 1.lproj/4_symbol.xml
mv 2.lproj/5_symbol.xml 2.lproj/14_symbol.xml
mv 2.lproj/6_symbol.xml 2.lproj/1_symbol.xml
mv 2.lproj/7_symbol.xml 2.lproj/3_symbol.xml
mv 2.lproj/8_symbol.xml 2.lproj/7_symbol.xml
mv 3.lproj/10_symbol.xml 3.lproj/10_symbol.xml
mv 3.lproj/11_symbol.xml 3.lproj/11_symbol.xml
mv 3.lproj/12_symbol.xml 3.lproj/2_symbol.xml
mv 3.lproj/9_symbol.xml 3.lproj/8_symbol.xml
mv 4.lproj/13_symbol.xml 4.lproj/13_symbol.xml
mv 4.lproj/14_symbol.xml 4.lproj/15_symbol.xml
mv 4.lproj/15_symbol.xml 4.lproj/9_symbol.xml
mv 4.lproj/16_symbol.xml 4.lproj/5_symbol.xml

Resources