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?
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 whale
dog dog cat cat bird bird whale whale
Alternatively as a shell function, e.g. to be reusable within a script:
duplicator() {
for i; do echo -n "$i $i "; done; echo
}
which can then be run directly where defined as
duplicator dog cat bird whale
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:
@FThe array
@Fcontains the fields of each line read in when autosplit mode is turned on. See perlrun for the-aswitch. This array is package-specific, and must be declared or given a full package name if not in package main when running understrict 'vars'.
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"
s="dog cat bird wale"
ss=$( tr ' ' '\n' <<< "$s" | sed p | tr '\n' ' ' )
echo "$ss"
dog dog cat cat bird bird wale wale
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).
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
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
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.