Find`s placeholder {} sed from variable1 and put into variable2 - linux

Then i use this script
for line in "`cat fromDirs.txt`";
do
find "$line" -type f \( -name '*good*' -o -exec grep -F "(NODES_'TASK')" {} \; \) -exec cp {} /tmp/ \;;
done
I get in folder /tmp only file names from where they are copied, but i need this filenames contains full paths from where they comes, im bored to trying fight with sed, please help
So i need just take each {} value and replace slash (/) with minus sign (-)
I trying many of variant but nothing good, this code do not work too
for line in "`cat fromDirs.txt`";
do
find "$line" -type f \( -name '*good*' -o -exec grep -F "(NODES_'TASK')" {} \; \) -exec cp {} /tmp/$(sed "s/\//-/g" <<< {}) \;;
done
file fromDirs.txt contains
/home/orders/
/etc/bin/school/
there are no output, just nothing haping, maybe beacause i use sh? i havent bash at all on system
I think the problem is in sed as it read placeholder {} as file instead of string, so if {} = /home/orders/good.php then sed open this file and change all slashes to minus sign, but i need to changeslashes only in filename so /home/orders/good.php -> -home-orders-good.php and then cp to /tmp/-home-orders-good.php

I guess you get problem since you double quote the output of the file.
Try change from:
for line in "`cat fromDirs.txt`";
to:
for line in `cat fromDirs.txt`;
or better (remove old and outdated back tics):
for line in $(cat fromDirs.txt);
best (use while to read the file):
#!/bin/bash
while read line
do
find "$line" -type f \( -name '*good*' -o -exec grep -F "(NODES_'TASK')" {} \; \) -exec cp {} /tmp/$(sed "s/\//-/g" <<< {}) \;;
done < fromDirs.txt

Related

How to capitalize only first letter in filename using tr or mv on Linux?

Using terminal I need to write a command with mv/tr/find to:
Find files with names that start with a, o or e: [a,o,e]*.
Change those filenames first letters from lower to capital using tr or mv.
Everything has to be in one command not script. I've tried something like this:
find -name "[a,o,e]*" -exec {} mv [a,o,e]* [A,O,E]* \;
but it doesn't work.
This is a sample with some of my files starting with letters p and g:
$ find . -name '[pg]*.py'
./pycurl-example.py
./pymusic.py
./gtkmenu.py
You could use something like bellow, but unfortunatelly will not stop on the first occurence - it will translate all letters found:
$ find . -name '[pg]*.py' |tr '[pg]' '[PG]'
./Pycurl-examPle.Py
./Pymusic.py
./Gtkmenu.Py
Sinc tr has not an option to stop at first occurence like sed, you can make a trick like this:
$ find . -name '[pg]*.py' -exec bash -c 'echo -n "${0:2:1}"|tr 'pg' 'PG' && echo "${0:3}"' {} \;
Pycurl-example.py
Pymusic.py
Gtkmenu.py
Or even simplier without tr but with pure bash (idea from #Nominal Animal comment)
$ find . -name '[pg]*.py' -exec bash -c 'fn="${0:2}";echo "${fn^}"' {} \;
Pycurl-example.py
Pymusic.py
Gtkmenu.py
Or if your purpose is to do a real rename:
$ find . -name '[p]*c.py' -exec bash -c 'fn="${0:2}";mv -v "${fn}" "${fn^}"' {} \;
renamed 'pymusic.py' -> 'Pymusic.py'
And a more sophisticated rename that will work with any path:
$ find . -name 'g*.sh' -exec bash -c 'pn="${0%/*}";fn="${0##*/}";mv -v $0 ${pn}/${fn^}' {} \;
renamed './kalitools/Bluelog/scripts/gen_oui.sh' -> './kalitools/Bluelog/scripts/Gen_oui.sh'
renamed './greptest.sh' -> './Greptest.sh'
find . -name '[aoe]*' -exec sh -c 'echo mv "$1" "$(echo "$1"|sed -E "s,(.*/)(.),\1\u\2,")"' _ {} ';'
Remove the first "echo" if it looks like it's going to do what you want.
In my tmp dir with all kinds of crap in it, some of the output is:
mv ./a b c ./A b c
mv ./a.awk ./A.awk
mv ./a.prop ./A.prop
mv ./a1234 ./A1234
mv ./event.logger ./Event.logger
mv ./example.txt ./Example.txt
mv ./link/a ./link/A
mv ./link/a~ ./link/A~
mv ./old.csv ./Old.csv
mv ./rename/one-dash ./rename/One-dash
Try this:
for f in `find . -name "*" -type f|sed "s/^.\///"`; do f1stc=$(echo ${f::1}|tr [a-z] [A-Z]); fallbut1stc=$(echo ${f:1}); nf="${f1stc}${fallbut1stc}"; mv $f ${nf} 2>/dev/null || true; done

Copy or move files to another directory based on partial names in a text file

I want to copy some files from directory A to directory B on basis of partial filenames which are in a text file.
I tried the command below, but it's not working
for fn in $(cat filename.txt); do find . -type -f -name '$fn*' \
-exec rsync -aR '{}' /tmp/test2 \;
File names are in format abcd-1234-opi.txt, rety-4567-yuui.txt. I have to grep numbers from file names and then copy those files to another folder, I have numbers in a text file.
Can anybody guide me?
If I understand your question correct:
for file in $(<./example); do cp "${file}" /tmp/test2/; done
for file in $(<./example); do find . -type f -name ${file} -exec rsync -aR {} /tmp/test2/ \; ; done
If understand correctly, the pattern to search for is the first 3 digits of the numbers after the first -, like this:
abd-12312-xyz
^^^
88-45644-oio
^^^
qwe-78908-678
^^^
Here's one way to write that:
while read line; do
pattern=${line#*-}
pattern=${pattern:0:3}
find . -type f -name "*$pattern*" -exec rsync -aR {} /tmp/test2 \;
done < patterns.txt
If your inputs are like "veeram_20171004-104805_APOLLO_9004060859-all.txt",
and you want to extract the "9004060859",
then you can try to find a different logic to extract that.
For example,
"cut off every after the last '-', and everything before the last '_'".
You can write like this:
pattern=${line%-*}
pattern=${pattern##*_}
Running find repeatedly should probably be avoided. Instead, factor the expressions into the find command line programmatically.
awk 'BEGIN { printf "find . -type -f \\("; sep="" }
{ printf "%s -name \047*%s*\047", sep, $0; sep=" -o" }
END { printf " \\) -exec rsync -aR '{}' /tmp/test2 \\;\n" }' filename.txt |
sh
Leave off the pipe to sh to test.

"find -type f -iname '*.txt' | xargs -I {} echo ""> {}" does not work

I am working on linux bash. Now I would like to clear the contents of all .txt files. However, this command "find -type f -iname '.txt' | xargs -I {} echo ""> {}" seems not to work. Any suggestions? Any ideas about better solutions?
I replaced echo with truncate in order to clear a file and used find's -exec instead of piping to xargs:
find . -type f -name "*.txt" -exec truncate -s 0 {} \;

Using both basename and full path in find -exec

I'm having an adventure in the world of bash scripting with find today.
Say I'm looking to copy any png file in any subdirectory of /home/mine/Pictures to /home/mine/pngcoppies and rename it "copy[basename]"using find and -exec. This would require me to use both the full path name and the basename in the same exec command. My problem is that I don't know how to get the basename. (See below)
find /home/mine -iname "*.png" -exec cp {} /home/mine/pngcoppies/copy{what_do_I_enter_here?} \;
Note: The above isn't actually what I'm doing, but it's a fundamental example of the issue, so a workaround using some other method to achieve the same ends wouldn't really apply here. The question is fundamentally about find -exec and its use of basenames.
Thanks in advance!
To see what is going on when you execute the find, just type set -xv
-x : Prints commands and their arguments as they are executed.
-v : Prints shell input lines as they are read.
Here is what I have :
find . -name "*.xml" -exec echo {} \;
Gives the output:
./log.xml
./svnLog.xml
And when I try :
set -xv
find . -name "*.xml" -exec echo {} \;
I get :
find . -name "*.xml" -exec echo {} \;
+ find . -name '*.xml' -exec echo '{}' ';'
./log.xml
./svnLog.xml
And then find execute echo passing the found filename instead of the litteral : '{}'
but when you add something to the {} like below :
find . -name "*.xml" -exec echo something{} \;
+ find . -name '*.xml' -exec echo 'something{}' ';'
something{}
something{}
Here the echo is executed twice for the 2 xml files that I have and since there is no more '{}' is the parameter list of the exec, it is not going to be replaced. so we got the echo 'something{}' for each file found.
To deal with this, you can think about executing echo passing to it the filename as parameter like for example :
sh -xvc 'echo sothing/$0' filename
We already know what is -x and -v. -c is to get the command from the string after it (man sh)
so the result is :
sh -xvc 'echo somthing/$0' filename
+ sh -xvc 'echo somthing/$0' filename
echo somthing/$0
+ echo somthing/filename
sothing/filename
I used 'echo somthing/$0' between ' ' so that $0 don't get expanded by the current shell. try it with " " and you will see the expantion of $0 ;)
So to get back to your 'problem', the find should be formatted as below:
find . -name "*.xml" -exec sh -xvc 'echo sothing/$0' {} \;
And we will get :
find . -name "*.xml" -exec sh -xvc 'echo sothing/$0' {} \;
+ find . -name '*.xml' -exec sh -xvc 'echo sothing/$0' '{}' ';'
echo sothing/$0
+ echo sothing/./log.xml
sothing/./log.xml
echo sothing/$0
+ echo sothing/./svnLog.xml
sothing/./svnLog.xml
As we can see know, the find is going to execute the shell cammand echo sothing/$0 passing to it '{}' (replaced by the filename found by find) so we get the desired echo sothing/./log.xml
set +xv to remove the verbose mode
and we can get :
find . -name "*.xml" -exec sh -c 'echo "cp $0 someWhereElse/$0"' {} \;
cp ./log.xml someWhereElse/./log.xml
cp ./svnLog.xml someWhereElse/./svnLog.xml
so in your case , you have just to execute the copy in a sub shell (add sh or bash or you favorit shell after the exec) and let find pass the filename as parapeter to the it ;)
find /home/mine -iname "*.png" -exec sh -c 'cp $0 /home/mine/pngcoppies/copy/$0' {} \;
Hope this can help, and execuse me for my English.
From man find:
"The -execdir primary is identical to the -exec primary with the exception that utility will be executed from the directory that holds the current file. The filename substituted for the string ``{}'' is not qualified."
find /home/mine -iname "*.png" -execdir cp {} /home/mine/pngcoppies/copy{} \;
try something like this :
find /home/mine -iname "*.png" -printf "%P\n " | xargs -I % -n1 cp % /home/mine/pngcoppies/copy%
To get basename you use
basename $your_full_path
To get that path before the basename
dirname $your_full_path
You need to combine the first two answers
I was tailing everything in one directory into another directory with
find . -type f -exec sh -c 'tail -n 1000 $0 >../tail1000/$0.tail' {} \;
The first Answer gives
cp ./log.xml someWhereElse/./log.xml
or for my tail command
find . -type f -exec sh -c 'tail -n 1000 $0 >../tail1000/$0.tail' {} \;
tail -n 1000 ./filenane > ../tail1000/./filenane.tail
which surprisingly works but does not look like a nice path and I expect there are cases where a path/./morepath does something unexpected with some command.
Combining the answers gives
find . -type f -execdir sh -c 'tail -n 1000 $0 >../tail1000/$0.tail' {} \;
which executes
tail -n 1000 filenane > ../tail1000/filenane.tail
And looks much more likely to give the expected results.

Unix command to insert a comment on the top of all files in a directory

I want to add a comment to all files in a directory in Unix. Please suggest a solution if there is any combination of commands I can use.
Using find and sed:
$ find . -maxdepth 1 -type f -exec sed -i '1i #comment' {} \;
This will add the line #comment to the top of all the files in the current directory
Try doing this using a simple shell concatenation :
for i in *; do
{ echo '# this is a comment'; cat "$i"; } > /tmp/_$$file &&
mv /tmp/_$$file "$i"
done
For the fun, try doing this using ed :
echo $'1i\n# comment\n.\nw\nq' | ed -s file.txt
Here-doc version :
ed -s file.txt <<EOF
1i
# comment
.
w
q
EOF
If you want to add a comment to all files with an extension (e.g. ".rb"):
find . -maxdepth 1 -type f -name "*.rb" -exec sed -i '1i #comment' {} \;
And recursive:
find . -type f -name "*.rb" -exec sed -i '1i #comment' {} \;

Resources