Shell command xargs & sed doesn't work - bash

I just want to batch modify the suffix of the files,but it doesn't work!
The command line I used as below:
ls *html | xargs -I{} echo "\`echo {} | sed 's/html/css/g'\`"
However, when I just used ls *html,it shows:
file1.html file2.html file3.html file4.html file5.html
used ls *html | sed 's/html/css/g',it shows as I expected!
like this:
file1.css file2.css file3.css file4.css file5.css
I work on Mac OS. Could anyone give me some suggestions?
Thans in advance.

Because the backquotes are in double quotes, it gets executed immediately by the shell and not by xargs on each file.
The result is the same as
ls *html | xargs -I{} echo "{}"
However, if you use single quotes, you run into other troubles. You end up having to do something like this:
ls *html | xargs -I{} sh -c 'echo `echo {} | sed '\''s/html/css/g'\''`'
but it gets to be a mess, and we haven't even got to the actual renaming yet.
Using a loop is a bit nicer:
for file in *html; do
newname=${file%html}css
mv "$file" "$newname"
done

Using GNU Parallel:
ls *html | parallel echo before {} after {.}.css

Related

Unable to pass quoted string to rm directly with xagrs

To find some file with grep and delete them with rm I tried following command -
$ ls | grep 2019 | xargs -i rm \"{}\"
That did not work. Got the following error message -
rm: cannot remove '"2019-05-10 00:00:00-TO-2019-05-10 23:59:59_PDT_disconnection_info.csv"': No such file or directory
Looks like xargs is taking quotes literally. So, tried echoing instead of passing directly -
ls | grep 2019 | xargs -i echo \"{}\" | xargs rm
This worked.
Why does not it work without echoing?
The proper quoting is done by xargs, there is no need to quote it again. Just:
... | xargs -i rm {}
Or better, because rm accepts multiple arguments, just do:
... | xargs rm
Why does not it work without echoing?
When not used with -i, -I, -d or similar, the xargs utility handles proper quoting in input with double or single quotes or escaping with a backslash. The quotes are removed by the second xargs and rm is passed unquoted string. From man xargs:
.... xargs reads
items from the standard input, delimited by blanks (which can be
protected with double or single quotes or a backslash)
Compare:
$ echo "\e\r\t\q\e" | xargs -t echo
echo ertqe
ertqe
Also see Why you shouldn't parse the output of ls(1).

GNU Parallel with sed wrong arguement as file

I want to change all occurrences of <ga/ to <. With xargs, this works fine:
ls | xargs sed -i 's/<ga\//</g'
GNU Parallel says that it's a direct replacement for xargs, but doing
ls | parallel sed -i 's/<ga\//</g'
results in
/bin/bash: ga//: No such file or directory
for each file in the directory. I'm sure that I'm just forgetting a {} or \; somewhere, but the answer still alludes me.
ls | parallel -q sed -i 's/<ga\//</g'

find with grep not working inside shell script

I am using the below command to find the file names and it works fine when execute from command line:
$AIX->: find . | xargs grep -l "BE00036"
./6281723219129
$AIX->:
But the same command is not working when execute from shell script(ksh):
$AIX->: ksh test.ksh
**find: bad option -l**
part of my code is:
Var="find . | xargs grep -l \"BE00036\"
print `$Var`
If you want to assign the output of a command to a variable, you can do
Var="$(find . | xargs grep -l \"BE00036\")"
print "$Var"
This below one works for me:
var=`find . | xargs grep -l 'BE00036'`
echo "$var"

how to pipe commands in ubuntu

How do I pipe commands and their results in Ubuntu when writing them in the terminal. I would write the following commands in sequence -
$ ls | grep ab
abc.pdf
cde.pdf
$ cp abc.pdf cde.pdf files/
I would like to pipe the results of the first command into the second command, and write them all in the same line. How do I do that ?
something like
$ cp "ls | grep ab" files/
(the above is a contrived example and can be written as cp *.pdf files/)
Use the following:
cp `ls | grep ab` files/
Well, since the xargs person gave up, I'll offer my xargs solution:
ls | grep ab | xargs echo | while read f; do cp $f files/; done
Of course, this solution suffers from an obvious flaw: files with spaces in them will cause chaos.
An xargs solution without this flaw? Hmm...
ls | grep ab | xargs '-d\n' bash -c 'docp() { cp "$#" files/; }; docp "$#"'
Seems a bit klunky, but it works. Unless you have files with returns in them I mean. However, anyone who does that deserves what they get. Even that is solvable:
find . -mindepth 1 -maxdepth 1 -name '*ab*' -print0 | xargs -0 bash -c 'docp() { cp "$#" files/; }; docp "$#"'
To use xargs, you need to ensure that the filename arguments are the last arguments passed to the cp command. You can accomplish this with the -t option to cp to specify the target directory:
ls | grep ab | xargs cp -t files/
Of course, even though this is a contrived example, you should not parse the output of ls.

xargs to execute a string - what am I doing wrong?

I'm trying to rename all files in current directory such that upper case name is converted to lower. I'm trying to do it like this:
ls -1|gawk '{print "`mv "$0" "tolower($0)"`"}'|xargs -i -t eval {}
I have two files in the directory, Y and YY
-t added for debugging, and output is:
eval `mv Y y`
xargs: eval: No such file or directory
if I execute the eval on its own, it works and moves Y to y.
I know there are other ways to achieve this, but I'd like to get this working if I can!
Cheers
eval is a shell builtin command, not a standalone executable. Thus, xargs cannot run it directly. You probably want:
ls -1 | gawk '{print "`mv "$0" "tolower($0)"`"}' | xargs -i -t sh -c "{}"
Although you're looking at an xargs solution, another method to perform the same thing can be done with tr (assuming sh/bash/ksh syntax):
for i in *; do mv $i `echo $i | tr '[A-Z]' '[a-z]'`; done
If your files are created by creative users, you will see files like:
My brother's 12" records
The solutions so far do not work on that kind of files. If you have GNU Parallel installed this will work (even on the files with creative names):
ls | parallel 'mv {} "$(echo {} | tr "[:upper:]" "[:lower:]")"'
Watch the intro video to learn more: http://www.youtube.com/watch?v=OpaiGYxkSuQ
You can use eval with xargs like the one below.
Note: I only tested this in bash shell
ls -1| gawk '{print "mv "$0" /tmp/"toupper($0)""}'| xargs -I {} sh -c "eval {}"
or
ls -1| gawk '{print "mv "$0" /tmp/"toupper($0)""}'| xargs -I random_var_name sh -c "eval random_var_name"
I generally use this approach when I want to avoid one-liner for loop.
e.g.
for file in $(find /some/path | grep "pattern");do somecmd $file; done
The same can be written like below
find /some/path | grep "pattern"| xargs -I {} sh -c "somecmd {}"

Resources