Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

I have a string like: dog cat bird whale

And I want to get dog dog cat cat bird bird whale whale

All the words are in the same line. Any idea?

share|improve this question
add comment

8 Answers 8

Another approach, also using only bash builtins

$ string="dog cat bird whale"
$ twix() { while [[ ! -z $1 ]]; do printf "%s %s " $1 $1; shift; done; }
$ twix $string
dog dog cat cat bird bird whale whale

I don't see any benefits compared to the top answer, just to show a slightly different way, which might be better suitable for some purposes.

share|improve this answer
    
echo is also a shell builtin in Bash (test type echo). –  Daniel Andersson Mar 12 at 9:25
    
@DanielAndersson : Sure, that's why I wrote "also using only bash builtins", having your nice (already +1'd) answer in mind. –  mpy Mar 12 at 18:01
    
OK, you can classify my comment as language confusion :-) . –  Daniel Andersson Mar 12 at 18:26
add comment

Slightly over-the-top, but a haskell answer:

$ ghc -e "getLine >>= putStrLn . unwords . (concatMap $ replicate 2) . words" <<<"dog cat bird whale"
dog dog cat cat bird bird whale whale
share|improve this answer
add comment

Adding to the family of solutions :-) .

duplicator.sh:

for i; do echo -n "$i $i "; done; echo

Make executable, and now:

$ ./duplicator.sh dog cat bird wale
dog dog cat cat bird bird wale wale

As a shell function, to be re-used in a script:

duplicator() {
    for i; do echo -n "$i $i "; done; echo
}

$ duplicator dog cat bird whale
dog dog cat cat bird bird whale whale
share|improve this answer
4  
I like the simplicity of the shell approach. –  Henk Langeveld Mar 7 at 17:27
    
Well I really love the simplicity of this solution –  Cristian Mar 10 at 16:41
    
Way better than the first couple of ways I thought of. –  Joe Mar 10 at 21:17
add comment

You could use sed:

sed -r 's/(\S+)/\1 \1/g' filename

If you want to save the changes to the file in-place, say:

sed -i -r 's/(\S+)/\1 \1/g' filename

You could also use perl:

perl -M5.10.0 -ne 'say join " ", map{$_, $_} split " ";' filename

(Add the -i option to save the changes to the file in-place.)

Or, as suggested by terdon:

perl -M5.10.0 -ane 'say join " ", map{$_, $_} @F;' filename

Quoting from perlvar:

@F

The array @F contains the fields of each line read in when autosplit mode is turned on. See perlrun for the -a switch. This array is package-specific, and must be declared or given a full package name if not in package main when running under strict 'vars'.

share|improve this answer
4  
Shorter: sed -r 's/\S+/& &/g'. –  cYrus Mar 6 at 18:53
2  
That is not a Bash script. Invoking some command (even from a Bash shell) does not consistute a Bash script. –  Peter Mortensen Mar 7 at 10:48
4  
@PeterMortensen You are technically correct (the best kind of correct), but practically every system that has bash installed will also have the standard unix tools, including sed and awk. The whole point of shell scripting (well, a large part of the point of it) is to point commands at the right place. –  evilsoup Mar 7 at 11:43
3  
@PeterMortensen This is incorrect. A bash script can call external commands. A bash script should start with a shebang line, but this is not strictly necessary. The question did not specify that the bash script should not call external commands (often called a “pure bash script”). –  Gilles Mar 7 at 12:20
2  
Nice perl trick. You can make it shorter with -a: perl -M5.10.0 -ane 'say join " ", map{$_, $_} @F;' –  terdon Mar 7 at 14:35
show 1 more comment

If you have your string in an a variable, say foo="dog cat bird whale", you could do:

  • Pure bash:

    $ echo "$foo" | (read a b c d && echo "$a $a $b $b $c $c $d $d")
    dog dog cat cat bird bird whale whale
    

    Explanation: The parentheses are needed so that the read and echo happen in the same subshell and can therefore share variables. Without them, the echo would just print a blank line.

  • coreutils:

    $ join -j 5 -o 1.1,1.1,1.2,1.2,1.3,1.3,1.4,1.4 <(echo $foo) <(echo)
    dog dog cat cat bird bird whale whale
    

    Explanation: The -o flag of join allows you to set the output format. Here, I'm telling it to print the 1st field of the 1st file (1.1), followed by the 2nd field of the 1st file (1.2) etc. That way each field of the 1st file is printed twice. However, join is designed to, well, join two input lines on a common field. So, I also pass it a blank line (<(echo)) and then ignore it. The -j sets the join field, setting it to one that does not exist (the 5th) causes join to print the entire line.

    If you don't care about whitespace or the input order, you could do

    $ paste <(echo $foo) <(echo $foo)
    dog cat bird wale   dog cat bird wale
    
  • Perl 1:

    $ echo $foo | perl -lane 'push @k, $_,$_ for @F; print "@k"'
    dog dog cat cat bird bird whale whale
    

    Explanation:

    -l: adds a newline to each print call (among other things)
    -a: turns on field splitting, fields are saved as @F
    -n: process input line by line
    -e: give a script as a command line parameter.
    

    The script above will save each field (from @F) twice in the array @k and then print @k. If you don't need the trailing newline, you could simplify to

    $ echo $foo | perl -ane 'print " $_ $_" for @F'
    
  • Perl 2:

    $ echo $foo | perl -0040 -pne 'print "$_"' | paste - - 
    dog dog cat cat bird bird whale whale
    

    Explanation: The -0 option sets the input record separator (as a hexadecimal or octal number, see here for conversions). Here, I am setting it to octal 040 which is a space. The -p makes perl print each input "line" and since we have set the record separator to space, lines are now defined by spaces, so every field is printed twice.

  • awk:

    $ echo $foo | awk '{for(i=1;i<=NF;i++){$i=$i" "$i;} 1;}'
    dog dog cat cat bird bird whale whale
    

    Explanation: NF is the number of fields, so the script above goes through each field and appends it to itself. Once that's done, we print the line (1; is just shorthand for print).

share|improve this answer
add comment

What would this be without an awk/gawk answer:

$ awk '{ for(i=1;i<=NF+1;i+=1/2) { printf("%s ",$i); }}' <<<"dog cat bird whale"
dog dog cat cat bird bird whale whale 

If a terminating newline is important:

$ awk '{ for(i=1;i<=NF+1;i+=1/2) { printf("%s ",$i); }} END{print ""}' <<<"dog cat bird whale"
share|improve this answer
    
My firsts downvote. Just curious, why? Is there something wrong with the script? Or a more abbreviated version? –  user19087 Mar 6 at 21:26
    
Not my downvote, but for(i=1;i<=NF;++i) printf "%s %s ",$i,$i; is both shorter and more readable, IMHO. –  rici Mar 6 at 22:12
    
Hard to argue with that, so I've simplified and shortened my answer (now takes advantage of indices being rounded down to ints) without changing the approach. Hopefully it is now clearer. –  user19087 Mar 7 at 0:01
1  
That is not a Bash script. Invoking some command (even from a Bash shell) does not consistute a Bash script. –  Peter Mortensen Mar 7 at 10:55
    
Putting this in a script is trivial, though. –  Kevin Mar 8 at 0:20
show 1 more comment

Now for a python answer:

From the command line:

$ python -c "import sys; s=sys.argv[1:]; print(' '.join(j for i in zip(s,s)for j in i));" dog cat bird whale

From stdin:

$ python -c "s=input().split(); print(' '.join(j for i in zip(s,s)for j in i));" <<<"dog cat bird whale"

The result in both cases:

dog dog cat cat bird bird whale whale
share|improve this answer
add comment
s="dog cat bird wale"
ss=$( tr ' ' '\n' <<< "$s" | sed p | tr '\n' ' ' )
echo "$ss"
dog dog cat cat bird bird wale wale 
share|improve this answer
1  
I thought about writing sed -n 'p;p' -- I thought that was more transparent about what it's doing. –  glenn jackman Mar 6 at 19:22
1  
You should add that to the answer! –  terdon Mar 7 at 1:47
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.