Есть ли переносимый способ сценария оболочки unix для объединения нескольких строк вместе с заданным разделителем, например:
$ strjoin --- foo bar baz quux
foo---bar---baz---quux
Конечно, я мог бы использовать один лайнер $ scripting_language или уродливый явный цикл в функции shellscript, но старые хакеры unix, вероятно, тоже нуждались в этом, поэтому кто-то сделал такую стандартную команду, о которой я не знаю где-то в прошлом, правда?
редактировать
В sed
во многих ситуациях, безусловно, самый простой, но он не работает, если строки могут содержать пробелы. И многие другие ответы также не справляются с этим. Есть ли какие-нибудь решения кроме $IFS
трюк, который обрабатывает пробелы (и все возможные символы в целом) и не требует написания полного цикла?
Для многосимвольного длинного разделителя вы можете использовать:
sed
(как уже было указано @Отметка)
$ echo foo bar baz quux | sed "s/ /---/g"
ex
$ echo foo bar baz quux | ex +"s/ /---/gp" -cq! /dev/stdin
$ ex +"s/ /---/gp" -scq! <(echo foo bar baz quux)
printf
(но он покажет дополнительный конечный разделитель)
$ printf "%s---" foo bar baz quux
используя следующую функцию оболочки (согласно этому SO сообщение):
join_by { local IFS="$1"; shift; echo "$*"; }
Использование:
$ join_by '---' foo bar baz quux
Для односимвольных разделителей вы можете использовать:
tr
echo foo bar baz quux | tr ' ' '-'
Perl не который комплекс для простых операций:
$ perl -e 's/ /---/g'
В дополнение к комментарию @ embobo (который, надеюсь, скоро станет ответом), perl
можно использовать для Трещина и присоединиться произвольные строки. Это сложнее, чем использовать sed
и на основе приведенного выше примера было бы большим перебором.
awk
версия:
function join(a, start, end, sep, result, i) {
sep = sep ? sep : " "
start = start ? start : 1
end = end ? end : sizeof(a)
if (sep == SUBSEP) # magic value
sep = ""
result = a[start]
for (i = start + 1; i <= end; i++)
result = result sep a[i]
return result
}
Назовите это с gawk
с участием --source
ваши строки:
$ gawk -f join.awk --source 'BEGIN { split("foo bar quux",a); print join(a,1,3,"---") }'
foo---bar---quux
Версия сценария оболочки:
function join() {
for i in "$@"; do
echo -n "$i""---"
done
echo
}
join foo bar baz quux
Назовите его и обрежьте последний разделитель:
$ ./join.sh | sed 's/\-\-\-$//'
foo---bar---baz---quux
lam
Вот пример использования lam
команда:
$ SEP="---"; lam <(echo foo) -s$SEP <(echo bar) -s$SEP <(echo baz) -s$SEP <(echo quux)
foo---bar---baz---quux
paste
Если разделитель состоит из одного символа, то paste
можно использовать команду:
$ printf "%s\n" foo bar baz quux | paste -sd-
foo-bar-baz-quux
Лучший метод, который я нашел, - это упомянутый вами уродливый явный цикл.
join(){
# If no arguments, do nothing.
# This avoids confusing errors in some shells.
if [ $# -eq 0 ]; then
return
fi
local joiner="$1"
shift
while [ $# -gt 1 ]; do
printf "%s%s" "$1" "$joiner"
shift
done
printf '%s\n' "$1"
}
Использование:
$ join --- foo bar baz quux
foo---bar---baz---quux
Протестировано с Bash, Dash и Zsh на Ubuntu и должно работать в других оболочках на основе Bourne.