Разбить каждую строку разделителем, прочитанным из другого файла, и сохранить все составные слова с разбивкой в ​​файле результатов

"$@"делает то же самое с позиционными параметрами, что "${foo[@]}"делает с переменной-массивом foo. Как сказал Стивен, вы можете использовать @в расширениях, которые берут фигурные скобки, например. обозначение среза "${@:1:2}"будет таким же, как "$1" "$2". (Отсутствие второго числа расширяет остальную часть списка, например. "${@:2}"расширяется до "$2"и всех позиционных параметров после этого.)

Это соответствует именованным переменным, здесь«имя» специального параметра — @, а $впереди просто запускает расширение. Обратите внимание, что фигурные скобки необязательны для простого раскрытия (без индексации или манипуляций со строками внутри ). Итак, "${@}"— это то же самое, что и "$@", и вы могли бы даже написать "${@:1}"с тем же эффектом, поскольку индексация @начинается с единицы, а не с нуля.

Но вы не можете использовать ${@[@]}или ${@[1]}. Это просто $@и $1соответственно. (В некотором смысле $@уже похож на ${args[@]}, поэтому вы не можете индексировать его дальше. )Это также означает, что если вы хотите проиндексировать позиционные параметры, вам нужно будет использовать расширение среза, например. ${@:i:1}чтобы получить элемент в позиции i .


Тем не менее, обратите внимание, что строки -для манипуляций с массивами или @применяются ко всем элементам по отдельности. Например. здесь ooудален из fooи из oo, но остался третий элемент @, он просто пустой:

$ set -- foo bar oo
$ printf ":%s:\n" "${@/oo}"
:f:
:bar:
::

Если вы действительно хотите удалить элемент, вам придется сделать что-то еще, например перебрать значения и удалить ненужное. Вероятно, это проще сделать с помощью именованного массива, чем пытаться сделать это только внутри $@. Скажем, что-то вроде этого:

a=();
for x in "$@"; do
    if [[ $x != oo ]]; then
        a+=("$x");
    fi;
done

Или любой из (намного лучших )ответов на этот вопрос:Как удалить позиционный параметр из $@

0
03.07.2020, 17:11
6 ответов

Если у вас есть awk, который позволяет использовать расширенные регулярные выражения для разделителя полей, вы можете сделать что-то вроде

$ awk '
    BEGIN{FS=""; while((getline < "delim") > 0){FS = FS=="" ? $0 : FS"|"$0}}
    NF>1 {for(i=1;i<=NF;i++) print $i}
' names
Abdel
Aziz
Abdel
Piza
Märie
Pierre

ПРИМЕЧАНИЕ. :возможно, было бы чище использовать набор символов [ '+-], а не чередование регулярных выражений |'|+|-(, а также устранило бы возможную путаницу в отношении того, является ли +литералом или квантором регулярного выражения ). Однако это потребует тщательной перетасовки записей, поскольку -внутри [...]является оператором диапазона, если только он не находится ни в начале, ни в конце.

2
18.03.2021, 23:22

Использование grep, trиsort:

Предостережение :Нам нужно переместить -в delimsвверх или вниз файла (или trбудет думать, что есть диапазон ).

Получить все строки, содержащие символы-разделители, с помощью grepи заменить все разделители на новые строки (получить все символы в delimsбез новых строк с помощьюtr -d '\n' < delims).

Передайте результат на sort -u, чтобы исключить дубликаты и перенаправить вывод на compounds.

grep -F -f delims names | tr -- "$(tr -d '\n' < delims)" '\n' | sort -u > compounds

Выход:

$ cat compounds
Abdel
Aziz
Märie
Pierre
Piza
0
18.03.2021, 23:22

Сначала мы создаем регулярное выражение на лету с помощью функции quotemeta, которая заключает в кавычки все специальные символы, а затем соединяем их с помощью регулярного выражения ИЛИ |. Мы пропускаем все строки в именах, которые не содержат хотя бы один элемент из регулярного выражения. В остальном мы разделяем их на регулярное выражение, а затем печатаем только уникальный элемент по одному в строке.

$ perl -lne '
    $re //= join "|", map { chomp;quotemeta; } <STDIN>;
    next unless /$re/;
    print for grep { ! $seen{$_}++ } split /$re/;
' names < delims

Abdel
Aziz
Piza
Märie
Pierre
0
18.03.2021, 23:22

Можно еще....

awk 'BEGIN{OFS=RS="";FS="\n"; getline;$1=$1;
       s=gsub("-","",$0);FS="["$0((s>0)?"-":"")"]";
       OFS=RS="\n"}
     NF>1{$1=$1; print}' delims names

Abdel
Aziz
Abdel
Piza
Märie
Pierre

Который устанавливает RSи FSтак, чтобы getlineчитал delimsкак один $0, а затем перекомпоновывает его с $1=$1, убеждаясь, что OFS=""не добавляет никаких непреднамеренных пробелов.

Затем мы можем немного повозиться gsub-покер с $0, чтобы убедиться, что -происходит только в конце набора символов (только добавление -в конец FS] если gsubуспешно и s>0), чтобы дать вам набор символов [ '+-]как FS.

Теперь мы можем установить RSобратно на \n, но мы также устанавливаем OFSна \n.

Тогда все будет как обычно, где бы NF>1но поскольку OFS="\n"нам не нужно перебирать NF, мы можем просто перекомпоновать с $1=$1иprint

0
18.03.2021, 23:22

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

re=$(paste -d '[]' /dev/null delims /dev/null | paste -sd '|')
dlm=$(paste -sd\| delims | sed -e 's/||/\n/;s/|//g;s/\n/|/;s:/:\\&:')
rpl=$(printf '\\n%.0s' $(seq ${#dlm})) 

sed -En \
  -e "/$re/ y/$dlm/$rpl/" \
  -e '/\n/p'              \
names | sort -u

Abdel
Aziz
Märie
Pierre
Piza
0
18.03.2021, 23:22

Это будет надежно и переносимо работать для 4 символов в вашем примере:

$ cat tst.awk
NR==FNR {
    FS = (NR > 1 ? FS "|" : "") "[" $0 "]"
    next
}
NF > 1 {
    for ( i=1; i<=NF; i++ ) {
        if ( !seen[$i]++ ) {
            print $i
        }
    }
}

.

$ awk -f tst.awk delims names
Abdel
Aziz
Piza
Märie
Pierre

Требуемая здесь сложность заключается в том, что у вас есть 3 метасимвола в вашем наборе из 4 разделителей:

  1. " "что означает «любая последовательность пробелов» в ФС,
  2. "+"что означает «1 или более повторений предыдущего выражения» в регулярном выражении (undefined, если оно находится в начале регулярного выражения или следует за |), и
  3. "-"что означает «диапазон», если он находится внутри выражения в квадратных скобках, а не в первом или последнем символе.

Таким образом, вы не можете просто создать|-разделенный список, например |-|+|', из символов в разделителях, поскольку тогда значение +не определено, а значение <blank>, если бы оно было само по себе, не было бы литерал, и вы не можете включить их все в квадратное выражение [ -+'], так как тогда значение -представляет собой диапазон символов от <blank>до +, опять же не буквальный.

То, что я делаю выше, — это создание|-разделенного списка выражений в квадратных скобках [ ]|[-]|[+]|['], так как это будет работать для любых/всех символов, которые могут содержаться в разделителях.

2
18.03.2021, 23:22

Теги

Похожие вопросы