13
1
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?
13
1
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?
28
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
21
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 understrict 'vars'
.
4Shorter: sed -r 's/\S+/& &/g'
. – cYrus – 2014-03-06T18:53:11.887
2That is not a Bash script. Invoking some command (even from a Bash shell) does not consistute a Bash script. – Peter Mortensen – 2014-03-07T10:48:57.193
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 – 2014-03-07T11:43:42.877
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 'SO- stop being evil' – 2014-03-07T12:20:40.357
2Nice perl trick. You can make it shorter with -a
: perl -M5.10.0 -ane 'say join " ", map{$_, $_} @F;'
– terdon – 2014-03-07T14:35:53.813
@terdon Added your suggestion. – devnull – 2014-03-07T14:42:35.913
4
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"
My firsts downvote. Just curious, why? Is there something wrong with the script? Or a more abbreviated version? – user19087 – 2014-03-06T21:26:08.747
Not my downvote, but for(i=1;i<=NF;++i) printf "%s %s ",$i,$i;
is both shorter and more readable, IMHO. – rici – 2014-03-06T22:12:46.997
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 – 2014-03-07T00:01:11.327
1That is not a Bash script. Invoking some command (even from a Bash shell) does not consistute a Bash script. – Peter Mortensen – 2014-03-07T10:55:53.400
Putting this in a script is trivial, though. – Kevin – 2014-03-08T00:20:05.840
I'd be leery about using a float as the increment. At some point you'll get 0.99999999 or 1.000000001 and you get one more/less iteration. – glenn jackman – 2014-03-30T14:31:23.403
3
s="dog cat bird wale"
ss=$( tr ' ' '\n' <<< "$s" | sed p | tr '\n' ' ' )
echo "$ss"
dog dog cat cat bird bird wale wale
1I thought about writing sed -n 'p;p'
-- I thought that was more transparent about what it's doing. – glenn jackman – 2014-03-06T19:22:31.170
1You should add that to the answer! – terdon – 2014-03-07T01:47:25.273
1
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).
0
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
0
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
0
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.
echo
is also a shell builtin in Bash (test type echo
). – Daniel Andersson – 2014-03-12T09:25:05.237
@DanielAndersson : Sure, that's why I wrote "also using only bash builtins", having your nice (already +1'd) answer in mind. – mpy – 2014-03-12T18:01:47.057
OK, you can classify my comment as language confusion :-) . – Daniel Andersson – 2014-03-12T18:26:14.490
4I like the simplicity of the shell approach. – Henk Langeveld – 2014-03-07T17:27:17.990
Well I really love the simplicity of this solution – Cristian – 2014-03-10T16:41:02.927
Way better than the first couple of ways I thought of. – Joe – 2014-03-10T21:17:59.980