Примечание: Спасибо Джеффу Шаллеру и steeldriver. Но поскольку ни один из них не опубликовал ответ, я не уверен, как отметить как решенный. Теперь я лучше понимаю каналы/подоболочки. Я почти уверен, что когда-то знал это, но прошло много времени с тех пор, как я пробовал что-то сложное в bash.
Оба присваивают отфильтрованный результат из awk переменной изамена процессау меня сработало. Мой окончательный код для чтения несортированных уникальных строк из stdin
:
while read -r FILE
do
...
done < <(awk '!x[$0]++')
Больше информации назамена процессадля тех, кто задается этим вопросом в поисках решения похожей проблемы.
ОРИГИНАЛЬНЫЙ ВОПРОС:
Я искал на сайте, но не смог найти ответ на свою проблему.
Я создаю массив из stdin и мне нужно отфильтровать уникальные строки. Чтобы сделать это, я использую awk '!x[$0]++'
то, что я прочитал, это сокращение для:
awk 'BEGIN { while (getline s) { if (!seen[s]) print s; seen[s]=1 } }'
.
Фильтр работает так, как и ожидалось, но проблема в том, что результирующий массив в результате while read
цикла пуст.
Например (используя $list
в качестве заменителя stdin
):
list=$'red apple\nyellow banana\npurple grape\norange orange\nyellow banana'
while read -r line; do
array[count++]=$line
done <<< "$list"
echo "array length = ${#array[@]}"
counter=0
while [ $counter -lt ${#array[@]} ]; do
echo ${array[counter++]}
done
производит:
array length = 5
red apple
yellow banana
purple grape
orange orange
yellow banana
Но фильтрация $list
с помощью awk:
list=$'red apple\nyellow banana\npurple grape\norange orange\nyellow banana'
awk '!x[$0]++' <<< "$list" | while read -r line; do
array[count++]=$line
done
echo "array length = ${#array[@]}"
counter=0
while [ $counter -lt ${#array[@]} ]; do
echo ${array[counter++]}
done
производит:
array length = 0
Но вывод awk '!x[$0]++' <<< "$list"
выглядит нормально:
red apple
yellow banana
purple grape
orange orange
Я попробовал проверить каждую строку в while read
цикле:
list=$'red apple\nyellow banana\npurple grape\norange orange\nyellow banana'
i=0
awk '!x[$0]++' <<< "$list" | while read -r line; do
echo "line[$i] = $line"
let i=i+1
done
и выглядит нормально:
line[0] = red apple
line[1] = yellow banana
line[2] = purple grape
line[3] = orange orange
Что я здесь упускаю?
Если это важно, я использую bash 3.2.57:
GNU bash, версия 3.2.57(1)-release (x86_64-apple-darwin15) Авторские права (C) 2007 Free Software Foundation, Inc.
решение1
awk '!x[$0]++' <<< "$list" |пока читаем -r строку; делаем множество[count++]=$строка сделанный
Thearray
(курсив) в данном случае является частьюsubshell
(смелый).
И $line
имеет $array
значениев то время какподоболочка, так сказать, живая.
После завершения работы подоболочки, то есть смерти, родительская (спаунерная) среда восстанавливается. Это включает в себя уничтожение любых переменных, установленных в подоболочке.
В этом случае:
удаленный,$array
удаленный.$line
Попробуй это:
list=$'red apple\nyellow banana\npurple grape\norange orange\nyellow banana'
awk '!x[$0]++' <<< "$list" | while read -r line; do
array[count++]=$line
printf "array[%d] { %s\n" ${#array[@]} # array[num_of_elements] {
printf " %s\n" "${array[@]}" # elements
printf "}\n" # } end of array
done
printf "\n[ %s ]\n\n" "END OF SUBSHELL (PIPE)"
printf "array[%d] {\n" ${#array[@]}
printf " %s\n" "${array[@]}"
printf "}\n"
Урожайность:
array[1] {
red apple
}
array[2] {
red apple
yellow banana
}
array[3] {
red apple
yellow banana
purple grape
}
array[4] {
red apple
yellow banana
purple grape
orange orange
}
[ END OF SUBSHELL (PIPE) ]
array[0] {
}
Или согласно инструкции.
Мы можем начать сТрубопроводы
[…] Каждая команда в конвейере выполняется по-своему.подоболочка(видетьСреда выполнения команд). […]
ИСреда выполнения командрасширяет приключение следующим образом:
[…] Команда, вызываемая в этомотдельная среда не могувлияют на среду выполнения оболочки.
Подстановка команд, команды, сгруппированные с помощью скобок, и асинхронные команды вызываются в среде подоболочки, которая является дубликатом среды оболочки, за исключением того, что ловушки, пойманные оболочкой, сбрасываются до значений, которые оболочка унаследовала от своего родителя при вызове. Встроенные команды, которые вызываются как часть конвейера, также выполняются в среде подоболочки.Изменения, внесенные в среду подоболочки, не могут повлиять на среду выполнения оболочки.[…]
Он не может повлиять: поэтому он не может установиться.
Однако мы можем перенаправить и сделать что-то в направлении:
list=$'red apple\nyellow banana\npurple grape\norange orange\nyellow banana'
while read -r line; do
arr[count++]=$line
done <<<"$(awk '!x[$0]++' <<< "$list")"
echo "arr length = ${#arr[@]}"
count=0
while [[ $count -lt ${#arr[@]} ]]; do
echo ${arr[count++]}
done
решение2
Некоторые решения вашей проблемыбез петли
# use bash's mapfile with process substitution
mapfile -t arr < <( awk '!x[$0]++' <<<"$list" )
# use array assignment syntax (at least bash, ksh, zsh)
# of a command-substituted value split at newline only
# and (if the data can contain globs) globbing disabled
set -f; IFS='\n' arr=( $( awk '!x[$0]++' <<<"$list" ) ); set +f