Bash Script для повторения каждого слова в строке?


13

У меня есть строка вроде: dog cat bird whale

И я хочу получить dog dog cat cat bird bird whale whale

Все слова в одной строке. Любая идея?

Ответы:


28

Добавление в семейство решений :-).

duplicator.sh:

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

Сделайте исполняемый файл и теперь:

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

В качестве альтернативы в качестве функции оболочки, например, для повторного использования в скрипте:

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

который затем может быть запущен непосредственно там, где определяется

duplicator dog cat bird whale

4
Мне нравится простота подхода оболочки.
Хенк Лангевельд,

Ну, я действительно люблю простоту этого решения
Cristian

Намного лучше, чем первые пару способов, о которых я думал.
Джо

21

Вы можете использовать sed:

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

Если вы хотите сохранить изменения в файле на месте, скажите:

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

Вы также можете использовать perl:

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

(Добавьте -iопцию, чтобы сохранить изменения в файле на месте.)

Или, как предполагает Тердон :

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

Цитирование из perlvar:

@F

Массив @Fсодержит поля каждой строки, считываемой при включении режима автоматического разделения. Смотрите perlrun для -aпереключения. Этот массив зависит от пакета и должен быть объявлен или иметь полное имя пакета, если он не находится в основном пакете при запуске strict 'vars'.


4
Короче: sed -r 's/\S+/& &/g'.
cYrus

2
Это не скрипт Bash. Вызов некоторой команды (даже из оболочки Bash) не состоит из сценария Bash.
Питер Мортенсен

4
@PeterMortensen Вы технически верны (самый лучший вариант), но практически в каждой системе, в которой установлен bash, также есть стандартные инструменты Unix, включая sed и awk. Весь смысл сценариев оболочки (ну, большая часть этого) состоит в том, чтобы указывать команды в нужном месте.
evilsoup

3
@PeterMortensen Это неверно. Скрипт bash может вызывать внешние команды. Сценарий bash должен начинаться со строки shebang, но это не является строго обязательным. В вопросе не указывалось, что сценарий bash не должен вызывать внешние команды (часто называемые «сценарием чистого bash»).
Жиль "ТАК ... перестать быть злым"

2
Хороший перл трюк. Вы можете сократить его с помощью -a:perl -M5.10.0 -ane 'say join " ", map{$_, $_} @F;'
terdon

4

Что бы это было без awk/gawkответа:

$ 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 

Если важен завершающий перевод строки:

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

Мой первый downvote. Просто любопытно, почему? Что-то не так с сценарием? Или более сокращенная версия?
user19087

Не мое понижение, но for(i=1;i<=NF;++i) printf "%s %s ",$i,$i;и короче, и более читабельно, ИМХО.
Ричи

Трудно с этим поспорить, поэтому я упростил и сократил свой ответ (теперь использует округление индексов до целых) без изменения подхода. Надеюсь, теперь все стало понятнее.
user19087

1
Это не скрипт Bash. Вызов некоторой команды (даже из оболочки Bash) не состоит из сценария Bash.
Питер Мортенсен

Однако поместить это в сценарий тривиально.
Кевин

3
s="dog cat bird wale"
ss=$( tr ' ' '\n' <<< "$s" | sed p | tr '\n' ' ' )
echo "$ss"
dog dog cat cat bird bird wale wale 

1
Я думал о написании sed -n 'p;p'- я думал, что это было более прозрачным в том, что он делает.
Гленн Джекман

1
Вы должны добавить это к ответу!
Terdon

1

Если у вас есть строка в переменной, скажем foo="dog cat bird whale", вы можете сделать:

  • Чистая Баш:

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

    Объяснение: Скобки нужны для того, чтобы readи echoпроисходили в одной и той же подоболочке и поэтому могли совместно использовать переменные. Без них echoпросто напечатать пустую строку.

  • 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

    Объяснение:-o флаг joinпозволяет установить формат вывода. Здесь я советую напечатать 1-е поле 1-го файла ( 1.1), затем 2-е поле 1-го файла ( 1.2) и т. Д. Таким образом, каждое поле 1-го файла печатается дважды. Тем не менее, joinон предназначен для объединения двух строк ввода в общее поле. Итак, я также пропускаю пустую строку ( <(echo)) и затем игнорирую это. В -jнаборах Соединить поле, установив его на тот , который не существует (5 - й) приводит joinк печати всей линии.

    Если вам не нужны пробелы или порядок ввода, вы можете сделать

    $ 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

    Объяснение:

    -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.

    Сценарий выше сохранит каждое поле (из @F) дважды в массиве, @kа затем напечатает @k. Если вам не нужен завершающий перевод строки, вы можете упростить до

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

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

    Объяснение:-0 опция устанавливает входной разделитель записей (как шестнадцатеричное или восьмеричное число, см здесь для преобразования). Здесь я устанавливаю восьмеричное значение, 040которое является пробелом. В -pмарке perlпечать каждого входа «линия» и с тех пор мы установили разделитель в пространство, линии теперь определяются пробелами, поэтому каждое поле печатаются дважды.

  • awk:

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

    Объяснение: NF это число полей, поэтому приведенный выше скрипт просматривает каждое поле и добавляет его к себе. Как только это будет сделано, мы печатаем строку ( 1;просто сокращение для печати).


0

Теперь для pythonответа:

Из командной строки:

$ 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

Из стандартного ввода:

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

Результат в обоих случаях:

dog dog cat cat bird bird whale whale

0

Немного чрезмерно, но haskellответ:

$ ghc -e "getLine >>= putStrLn . unwords . (concatMap $ replicate 2) . words" <<<"dog cat bird whale"
dog dog cat cat bird bird whale whale

0

Другой подход, также использующий только встроенные команды bash

$ 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

Я не вижу никаких преимуществ по сравнению с основным ответом, просто чтобы показать немного другой способ, который может быть лучше подходит для некоторых целей.


echoэто также оболочка, встроенная в Bash (тест type echo).
Даниэль Андерссон

@DanielAndersson: Конечно, именно поэтому я написал « также использую только встроенные средства bash», имея в виду ваш хороший (уже +1) ответ.
mpy

ОК, вы можете отнести мой комментарий к языковой путанице :-).
Даниэль Андерссон
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.