Substitution in renaming file - linux

I need help with the following:
I have these files with names: ABC.rt1, ABC.rt2, ... , ABC.rt8
I should rename all of them in the following way: if the number, let us say i at the end .rt${i} is odd, then replace the extension by appendig AB, else CD. For example I would expect these output:
ABC.rt1 --> ABC.rt1-AB
ABC.rt2 --> ABC.rt1-CD ... etc.
I tried to use this simple script, but does not work:
for i in `seq 1 8`; do mv -v ./ABC.rt${i} ./ABC.rt${if [ $(( $i % 2 )) -eq 0 ] ; then echo ${i}-CD; else echo ${i}-AB;fi};done
Can you help me?

In this specific example it might be easier to make two loops?
for i in *[1357];do mv -v $i $i-AB;done
for i in *[2468];do mv -v $i $i-CD;done
If its a requirement to create a general solution please let me know and I'll help more.

Try:
for i in `seq 1 8`; do
if [ $(( $i % 2 )) -eq 0 ] ; then
newext=${i}-CD
else
newext=${i}-AB
fi
mv -v ./ABC.rt${i} ./ABC.rt$newext
done
Don't try and cram all that in a single line, it's unreadable and your syntax ends up being incorrect.

perl -e'while(<*.rt?>) { rename $_, $_.($1 % 2 ? "-AB" : "-CD") if /\.rt(\d)$/ }'
Before:
a.rt1 a.rt2
After:
a.rt1-AB a.rt2-CD
Note: it is different from the example in your question.

Here you go, all bash, short and concise:
for f in *[1-8]
do
mv $f $f-`echo $f | grep -q '[1357]$' && echo AB || echo CD`
done

Related

Linux find with symlink recursion and some extras

I'm currently monitoring a number of dirs for log files; specifically those just created. It's been a long time since my Linux and after some trial and error I've hacked together what I need but it takes a full 20secs or more to return. I'm hoping I can have an expert look at it and advise me on something a little more streamlined.
find . -type f -follow -print | xargs ls -ltr 2>/dev/null | grep '2\?10' | tail
So for example find the last 10 files matching the name. Optimally I'd like to turn this into a bash script that accepts one argument and replaces the grep expression but I figure one thing at a time.
Thanks for your help in advance!
I bit the bullet and wrote the script; I'll tinker with it more later.
#!/bin/bash
if [ $# != 2 ]; then
echo findLog Usage: findLog [3 digit cluster] [pick 1: main message service detail soap]
exit 0
fi
if [ "$2" == "service" ]; then
file="$2-time-"
elif [ "$2" == "detail" ]; then
file="$2-time-"
else file="$2-"
fi
cluster="$1"
#store logpaths for readability
a="/pathto/A"
b="/pathto/B"
c="/pathto/C"
d="/pathto/D"
e="/pathto/E"
f="/pathto/F"
g="/pathto/G"
h="/pathto/H"
logpaths=( $a $b $c $d $e $f $g $h )
for i in "${logpaths[#]}"
do
ls -ltr "$i"/*.log | grep "$file"${cluster:0:1}${i: -1}${cluster: -2}
done

Linux: How do I compare all files in a directory against each other?

For my homework, I have to check if two files in a directory have the same contents and if so, replace one with a hardlink to the other. My script looks like:
cd $1 # $1 is the directory this script executes in
FILES=`find . -type f`
for line1 in $FILES
do
for line2 in $FILES
do
(check the two files with cmp)
done
done
My problem is that I can not figure out the conditional expression to make sure that the two files are not the same: If the directory has files a,b,c, and d it should not return true for checking a against a. How do I do this?
Edit: So I've got this:
cmp $line1 $line2 > /dev/null
if [ $? -eq 0 -a "$line1" != "$line2" ]
But it counts the files twice: It checks a and b, and then b and a. for some reason, using < with strings does not work.
Edit: I think I figured it out, the solution is to use a \ before the <
Using test, or its alias [:
if [ "$line1" < "$line2" ]
then
check the files
fi
Note that I'm using < here instead of != (which would otherwise work) so that, once you've compared a with b, you won't later compare b with a.
Here is an optimized way to do it that also properly handle files with embedded spaces or similar:
find . -type f -exec sh -c '
compare() {
first=$1
shift
for i do
cmp -s "$first" "$i" || printf " %s and %s differ\n" "$first" "$i"
done
}
while [ $# -gt 1 ]; do
compare "$#"
shift
done ' sh {} +

Grep multiple bash parameters

I'm writing a bash script which shall search in multiple files.
The problem I'm encountering is that I can't egrep an undetermined number of variables passed as parameters to the bash script
I want it to do the following:
Given a random number of parameters. i.e:
./searchline.sh A B C
Do a grep on the first one, and egrep the result with the rest:
grep "A" * | egrep B | egrep C
What I've tried to do is to build a string with the egreps:
for j in "${#:2}";
do
ADDITIONALSEARCH="$ADDITIONALSEARCH | egrep $j";
done
grep "$1" * "$ADDITIONALSEARCH"
But somehow that won't work, it seems like bash is not treating the "egrep" string as an egrep.
Do you guys have any advice?
By the way, as a side note, I'm not able to create any auxiliary file so grep -f is out of the line I guess. Also note, that the number of parameters passed to the bash script is variable, so I can't do egrep "$2" | egrep "$3".
Thanks in advance.
Fernando
You can use recursion here to get required number of pipes:
#!/bin/bash
rec_egrep() {
if [ $# -eq 0 ]; then
exec cat
elif [ $# -eq 1 ]; then
exec egrep "$1"
else
local pat=$1
shift
egrep "$pat" | rec_egrep "$#"
fi
}
first_arg="$1"
shift
grep "$first_arg" * | rec_egrep "$#"
A safe eval can be a good solution:
#!/bin/bash
if [[ $# -gt 0 ]]; then
temp=("grep" "-e" "\"\$1\"" "*")
for (( i = 2; i <= $#; ++i )); do
temp=("${temp[#]}" "|" "egrep" "-e" "\"\$$i\"")
done
eval "${temp[#]}"
fi
To run it:
bash script.sh A B C

looking for a command to tentatively execute a command based on criteria

I am looking for a command (or way of doing) the following:
echo -n 6 | doif -criteria "isgreaterthan 4" -command 'do some stuff'
The echo part would obviously come from a more complicated string of bash commands. Essentially I am taking a piece of text from each line of a file and if it appears in another set of files more than x (say 100) then it will be appended to another file.
Is there a way to perform such trickery with awk somehow? Or is there another command.. I'm hoping that there is some sort of xargs style command to do this in the sense that the -I% portion would be the value with which to check the criteria and whatever follows would be the command to execute.
Thanks for thy insight.
It's possible, though I don't see the reason why you would do that...
function doif
{
read val1
op=$1
val2="$2"
shift 2
if [ $val1 $op "$val2" ]; then
"$#"
fi
}
echo -n 6 | doif -gt 3 ls /
if test 6 -gt 4; then
# do some stuff
fi
or
if test $( echo 6 ) -gt 4; then : ;fi
or
output=$( some cmds that generate text)
# this will be an error if $output is ill-formed
if test "$output" -gt 4; then : ; fi

Bash Comparing strings in a directory

Hi I am trying to compare two strings in a directory. the format is as follows.
{sametext}difference{sametext}.
Note: {sametext} is not static for each file
for example
myfile_1_exercise.txt compared to myfile_2_exercise.txt
Can you tell me how I would match the above strings in an if statement.
Basically I need to know how I would ignore the number in the two strings so that these would be same.
Some example code is shown below:
My example code looks like this:
for g in `ls -d */`;
do
if [ -d $g ]; then
cd $g # down 1 directories
for h in `ls *root`;
do
printf "${Process[${count}]} = ${basedir}/${f}${g}${h}\n"
h1=${h}
if [ "${h1}" = "${h2}" ]; then # NEED to MATCH SOME HOW??????????
echo we have a match
fi
h2=${h1}
let count+=1
done
cd ../
#printf "\n\n\n\n"
fi
done
What should be the test to determine this instead of "${h1}" = "${h2}"?
Cheers,
Mike
"myfile_1_exercise.txt" == "myfile_2_exercise.txt"
You mean the above test should return true (ignoring the numbers) right?
This is what I would have done:
h1="myfile_1_exercise.txt"
h2="myfile_2_exercise.txt"
if [ $( echo ${h1} | sed 's/[0-9]*//g' ) == $( echo ${h2} | sed 's/[0-9]*//g' ) ] ; then
# do something here.
fi
sed comes in handy here.
This basically goes through every file in the directory, extracts the two strings from the filename, and keeps a list of all the unique combinations thereof.
Then, it walks through this list, and uses bash's wildcard expansion to allow you to loop over each collection.
EDIT: Got rid of an ugly hack.
i=0
for f in *_*_*.txt
do
a=`echo "$f" | sed 's/\(.*\)_.*_\(.*\).txt/\1/g'`
b=`echo "$f" | sed 's/\(.*\)_.*_\(.*\).txt/\2/g'`
tmp=${all[#]}
expr match "$tmp" ".*$a:$b.*" >/dev/null
if [ "$?" == "1" ]
then
all[i]="$a:$b"
let i+=1
fi
done
for f in ${all[#]}
do
a=`echo "$f" | sed 's/\(.*\):\(.*\)/\1/g'`
b=`echo "$f" | sed 's/\(.*\):\(.*\)/\2/g'`
echo $a - $b
for f2 in $a_*_$b.txt
do
echo " $f2"
# ...
done
done
Of course, this assumes that all the files you care about follow the *_*_*.txt pattern.
Disclaimer:
Mileage can vary and you may have to adjust and debug the script for corner cases.
You may be better off using Perl for your task.
There could be better solutions even in Bash. This one is not very efficient but it seems to work.
Said that, here is a script that compares two strings according to your requirements. I am sure you can figure how to use it in your directory listing script (for which you may want to consider find by the way)
This script takes two strings and prints match! if they match
$ bash compare.sh myfile_1_exercise.txt myfile_2_exercise.txt
match!
$ bash compare.sh myfile_1_exercise.txt otherfile_2_exercise.txt
$
The script:
#!/bin/bash
fname1=$1
fname2=$2
findStartMatch() {
match=""
rest1=$1 ;
rest2=$2 ;
char1=""
char2=""
while [[ "$rest1" != "" && "$rest2" != "" && "$char1" == "$char2" ]] ; do
char1=$(echo $rest1 | sed 's/\(.\).*/\1/');
rest1=$(echo $rest1 | sed 's/.\(.*\)/\1/') ;
char2=$(echo $rest2 | sed 's/\(.\).*/\1/');
rest2=$(echo $rest2 | sed 's/.\(.*\)/\1/') ;
if [[ "$char1" == "$char2" ]] ; then
match="${match}${char1}"
fi
done
}
findEndMatch() {
match=""
rest1=$1 ;
rest2=$2 ;
char1=""
char2=""
while [[ "$rest1" != "" && "$rest2" != "" && "$char1" == "$char2" ]] ; do
char1=$(echo $rest1 | sed 's/.*\(.\)/\1/');
rest1=$(echo $rest1 | sed 's/\(.*\)./\1/') ;
char2=$(echo $rest2 | sed 's/.*\(.\)/\1/');
rest2=$(echo $rest2 | sed 's/\(.*\)./\1/') ;
if [[ "$char1" == "$char2" ]] ; then
match="${char1}${match}"
fi
done
}
findStartMatch $fname1 $fname2
startMatch=$match
findEndMatch $fname1 $fname2
endMatch=$match
if [[ "$startMatch" != "" && "$endMatch" != "" ]] ; then
echo "match!"
fi
If you are actually comparing two files like you mentiond ... probably you can use diff command like
diff myfile_1_exercise.txt myfile_2_exercise.txt

Resources