Reputation: 2000
I'll explain this problem with an example. Let's say I want to generate a list with two columns. Column 1 contains a file name and column 2 contains the md5 sum of that file.
This can be done using the following script:
for FILE in `ls -p | grep -v "/"`;
do
printf "%s %s\n" "$FILE" `md5 -q "$FILE"`;
done;
Can this be done on a single line using pipes? I've tried using different combinations of sed
, xargs
and printf
but I think I'm missing something. Here's one of my attempts:
ls -p | grep -v "/" | xargs -I FILE printf "%s %s\n" FILE `md5 -q FILE`
In that attempt, the FILE
inside the backticks isn't substituted, which isn't really surprising.
Is this the kind of thing where I should just use a multi-line script? Since there's no logic or control flow, I feel that a one-liner should be possible and that perhaps I'm not using my tools properly or I haven't found the right tool.
Apologies for the title, I have no idea what to call this question.
Upvotes: 4
Views: 2357
Reputation: 3540
To take care of concerns regarding file names with weird characters in the name, that require escaping, EXCEPT "
character, or file names with md5 sums and spaces as part of their names:
find . -maxdepth 1 -type f -print0 |xargs -0 -n1 -I{} bash -c 'echo -n "{} " ; cat "{}" |md5sum' | sed 's/ -$//'
Using cat
makes the filename for md5sum
the standard input (-
) which we strip off with sed
. However, no complex regexps required to change the order of the columns, as we use echo -n
to print the filename as the first column of the output.
Example:
samveen@precise:/tmp/tmp.JpmDNkhcRG$ touch "ab'c"
samveen@precise:/tmp/tmp.JpmDNkhcRG$ touch "ab d41d8cd98f00b204e9800998ecf8427e c"
samveen@precise:/tmp/tmp.JpmDNkhcRG$ touch "ab c"
samveen@precise:/tmp/tmp.JpmDNkhcRG$ touch "abc"
samveen@precise:/tmp/tmp.JpmDNkhcRG$ touch "ab
> c"
samveen@precise:/tmp/tmp.JpmDNkhcRG$ touch "ab\!c"
samveen@precise:/tmp/tmp.JpmDNkhcRG$ find . -maxdepth 1 -type f -print0 |xargs -0 -n1 -I{} bash -c 'echo -n "{} " ; cat "{}" |md5sum'| sed 's/ -$//'
./ab'c d41d8cd98f00b204e9800998ecf8427e
./ab d41d8cd98f00b204e9800998ecf8427e c d41d8cd98f00b204e9800998ecf8427e
./ab c d41d8cd98f00b204e9800998ecf8427e
./abc d41d8cd98f00b204e9800998ecf8427e
./ab
c d41d8cd98f00b204e9800998ecf8427e
./ab\!c d41d8cd98f00b204e9800998ecf8427e
Upvotes: 1
Reputation: 98118
You can use md5sum
, because I don't have md5
:)
ls -p | grep -v / | xargs md5sum | awk '{print $2,$1}'
and this may be a safer, more robust way:
find -maxdepth 1 -type f -exec md5sum {} \; | awk '{s=$2; $2=$1; $1=s;}1'
this should work with md5:
find -maxdepth 1 -type f -exec md5sum {} \; | sed 's/[^(]*(\([^)]*\)) =/\1/'
Upvotes: 6
Reputation: 99172
How about:
ls *.cc | xargs -I FILE md5 FILE | sed 's/^MD5 (\(.*\)) =/\1/'
Or if you're worried about spaces in your filenames (which I don't think you should allow yourself in any case),
find . -name "*.cc" -maxdepth 1 | xargs -I FILE md5 FILE | sed 's/^MD5 (\(.*\)) =/\1/'
Upvotes: 3
Reputation: 782509
ls -p | grep -v "/" | xargs -I FILE sh -c 'printf "%s %s\n" $0 `md5 -q $0`' FILE
Your code wasn't working because the backticks are processed by the original shell before passing the arguments to xargs
. You need to protect them from processing by enclosing them in quotes, and then passing that argument to sh
.
However, this seems like a bad way to do it. It doesn't work for files with spaces in the names, and grep
is a silly way to skip directories.
for FILE in *
do
if [ -f "$FILE" ]
then printf "%s %s\n" "$FILE" `md5 -q "$FILE"`
done
Upvotes: 1