12

I would like to change the format of the output xargs displays

cat k.txt 
1 
2 
3 

And

cat k.txt | xargs 
1 2 3

However I would like to have 1, 2, 3 or 1|2|3. Any suggestions?

guido
  • 8,649
Avinash
  • 403
  • That could be achieved with something (not exactly this) like xargs cat -n, but is easier if you just trim the newline characters, this can be achieved with echo $(cat), grep or awk (crawl the way to do it). xargs don't fit well for your current purpose. – 41754 Sep 02 '13 at 10:13

4 Answers4

21

Below are a dozen or so examples of how you can take a file such as this:

$ cat k.txt
1
2
3

and convert it to this format:

1,2,3

You can use this command to create the above file if you'd like to play along:

$ cat <<EOF > k.txt
1
2
3
EOF

The examples below are split into 2 groups. Ones that "work" and ones that "almost" work. I leave these because often times it's just as valuable to see why something doesn't work, as it is to see why something does.

Most scripting languages that I'm familiar with are represented. Some are represented multiple times, since as with the famous acronym typically referenced in Perl, TIMTOWTDI.

NOTE: You can swap out the comma (,) in the examples below and replace it with whatever characters you want, i.e. |.

Examples that "work"

These code snippets will produce the desired output.

The paste command:

$ paste -s -d ',' k.txt 
1,2,3

The sed command:

$ sed ':a;N;$!ba;s/\n/,/g' k.txt
1,2,3

$ sed ':a;{N;s/\n/,/};ba' k.txt 
1,2,3

The perl command:

$ perl -00 -p -e 's/\n(?!$)/,/g' k.txt
1,2,3

$ perl -00 -p -e 'chomp;tr/\n/,/' k.txt
1,2,3

The awk command:

$ awk '{printf"%s%s",c,$0;c=","}' k.txt
1,2,3

$ awk '{printf "%s,",$0}' k.txt | awk '{sub(/\,$/,"");print}'
1,2,3

$ awk -vORS=, 1 k.txt | awk '{sub(/\,$/,"");print}'
1,2,3

$ awk 'BEGIN {RS="dn"}{gsub("\n",",");print $0}' k.txt | awk '{sub(/\,$/,"");print}'
1,2,3

The python command:

$ python -c "import sys; print sys.stdin.read().replace('\n', ',')[0:-1]" <k.txt
1,2,3

$ python -c "import sys; print sys.stdin.read().replace('\n', ',').rstrip(',')" <k.txt
1,2,3

Bash's mapfile built-in:

$ mapfile -t a < k.txt; (IFS=','; echo "${a[*]}")
1,2,3

The ruby command:

$ ruby -00 -pe 'gsub /\n/,",";chop' < k.txt
1,2,3

$ ruby -00 -pe '$_.chomp!"\n";$_.tr!"\n",","' k.txt
1,2,3

The php command:

$ php -r 'echo strtr(chop(file_get_contents($argv[1])),"\n",",");' k.txt
1,2,3

Caveats

Most of the examples above will work just fine. Some have hidden issues, such as the PHP example above. The function chop() is actually an alias to rtrim(), so the last line's trailing spaces will also be removed.

So too do does the first Ruby example, and the first Python example. The issue is with how they're all making use of a type of operation that essentially "chops" off, blindly, a trailing character. This is fine in for the example that the OP provided, but care must be taken when using these types of one liners to make sure that they conform with the data they're processing.

Example

Say our sample file, k.txt looked like this instead:

$ echo -en "1\n2\n3" > k.txt

It looks similar but it has one slight difference. It doesn't have a trailing newline (\n) like the original file. Now when we run the first Python example we get this:

$ python -c "import sys; print sys.stdin.read().replace('\n', ',')[0:-1]" <k.txt
1,2,

Examples that "almost" work

These are the "always a bridesmaid, never a bride" examples. Most of them could probably be adapted, but when working a potential solution to a problem, when it feels "forced", it's probably the wrong tool for the job!

The perl command:

$ perl -p -e 's/\n/,/' k.txt
1,2,3,

The tr command:

$ tr '\n' ','  < k.txt 
1,2,3,

The cat + echo commands:

$ echo $(cat k.txt)
1 2 3

The ruby command:

$ ruby -pe '$_["\n"]=","' k.txt
1,2,3,

Bash's while + read built-ins:

$ while read line; do echo -n "$line,"; done < k.txt
1,2,3,
slm
  • 369,824
  • 1
    Regarding awk I prefer a shorter alternative: awk -vORS=, 1 k.txt – manatwork Sep 02 '13 at 12:39
  • Not sure whether CentOS has bash: mapfile -t a < k.txt; (IFS=','; echo "${a[*]}") – manatwork Sep 02 '13 at 12:42
  • not sure about the trailing newline. According to my test your perl and first awk has them too. – manatwork Sep 02 '13 at 12:44
  • I would put the paste one at the top. That's exactly what paste -s is for here. It's a standard command and would be the most efficient. All the other ones are overkill and/or are not portable or have limitations. – Stéphane Chazelas Sep 02 '13 at 12:46
  • mapfile is relatively new, added in bash 4.0. – manatwork Sep 02 '13 at 12:48
  • For awk without trailing separator maybe this is nicer: awk '{printf"%s%s",c,$0;c=","}' k.txt – manatwork Sep 02 '13 at 12:52
  • Nice! You can dodge the final newline in the perl version by setting the record seperator to slurp the whole file into one line, then use a negative lookahead in the regexp: perl -00 -p -e 's/\n(?!$)/,/g' k.txt yields 1,2,3. – goldilocks Sep 02 '13 at 13:29
  • ruby without trailing separator: ruby -00 -pe '$_.chomp!"\n";$_.tr!"\n",","' k.txt. (The same in perl, to avoid regular expressions: perl -00 -p -e 'chomp;tr/\n/,/' k.txt) – manatwork Sep 02 '13 at 13:50
  • I would not just chop unconditionally. If the input file has no trailing newline, your ruby's output will be “1,2,”. – manatwork Sep 02 '13 at 14:38
  • I think we skipped php: php -r 'echo strtr(chop(file_get_contents($argv[1])),"\n",",");' k.txt (With a note: php's chop() is actually alias to rtrim(), so the last line's trailing spaces will also be removed.) – manatwork Sep 02 '13 at 14:49
  • @manatwork - no we didn't 8-) – slm Sep 02 '13 at 14:52
  • Ok, one more thing: the [0:-1] in the python code works like the chop() in ruby: removes the last character unconditionally, even if is not newline. rstrip("\n") only trailing newlines (and rstrip() removes all trailing spaces). – manatwork Sep 02 '13 at 15:32
  • @manatwork - they're like rubiks cubes, can't put them down can you? – slm Sep 02 '13 at 16:08
  • @manatwork - I'll add your suggests and fixes today/suggestions today in a bit. Give a couple of hours, I'll drop a note so you can review. – slm Sep 03 '13 at 13:12
  • @manatwork - I moved all your examples up into the answer. – slm Sep 04 '13 at 07:03
8

@slm already given nice answer, but as your question "format output of xargs"

xargs -I{} echo -n "{}|" < test.txt
  • -I is "replace strings" option.
  • {} is a placeholder for output text.
  • This is similar to the use of a curly-bracket pair in "find."

If you want to get rid of the trailing | you can use sed to do some clean up:

$ xargs -I{} echo -n "{}|" < k.txt  | sed -e 's/|$//'
1|2|3
slm
  • 369,824
Rahul Patil
  • 24,711
3

This is an old thread, I know. The OP asked with a simple code like this. To keep it close to the original, I have a simple solution.

cat k.txt | xargs
1 2 3

use sed

cat k.txt | xargs | sed 's/ /,/g'
1,2,3

or

cat k.txt | xargs | sed 's/ /|/g'
1|2|3

sed can look a little weird but broken down, it makes much sense.

's is for substitute. g' is for global : without this, it will only do the first substitution on each new line. Since you use 'xargs' it displays them as one line. So you will get "1,2 3".

The delimiter is used for separation. I used the / character. One interesting trick: you can replace the delimiter with most any other character, as long as we keep it the same format between the quotes. So this would work also....

cat k.txt | xargs | sed 's# #,#g'

or

cat k.txt | xargs | sed 'sT T,Tg'

Obviously, using certain chars as the delimiter can get confusing, so be smart.

Rui F Ribeiro
  • 56,709
  • 26
  • 150
  • 232
0
xargs <k.txt | tr \  \|

You don't need cat - just pass in the file input and - if given no other command - xargs will pass out its default format - which is of a kind with /bin/echo's (without backslash c-escape interpretations).

xargs will strip off head/tail whitespace from the input file and squeeze other sequences of whitespace down to a single space. This means that while passing the file from tr to xargs like:

tr \\n \| <k.txt | xargs

...prints...

1|2|3|

...going the other way and operating only on the args that xargs space-delimits does....

1|2|3\n

...because xargs prints the final trailing newline (as is required for a text file), but it doesn't get translated that way.

Note, though, that this (or any other solution offered here) does not account for xargs quoting in input. xargs will pass out literally single/double/backslash-quoted non-newline whitespace chars in input, which can themselves be quoted like:

xargs <<\IN
1    2'      3'\'      \'4
IN

1 2      3' '4
mikeserv
  • 58,310