Назад | Перейти на главную страницу

портативный способ unix для объединения строк с разделителем

Есть ли переносимый способ сценария оболочки 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.