我和这里的问题差不多。
我有一个包含aa ab aa ac aa ad
等的数组。现在,我想从该数组中选择所有唯一元素。思想,这将是简单的用sort | uniq
或sort -u
因为他们在其他问题中提到,但没有在数组中改变...的代码是:
echo `echo "${ids[@]}" | sort | uniq`
我究竟做错了什么?
Answers:
有点hacky,但是应该这样做:
echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '
要将排序后的唯一结果保存回数组中,请执行数组分配:
sorted_unique_ids=($(echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))
如果您的外壳支持herestrings(bash
应该),则可以echo
通过将其更改为以下内容来节省进程:
tr ' ' '\n' <<< "${ids[@]}" | sort -u | tr '\n' ' '
输入:
ids=(aa ab aa ac aa ad)
输出:
aa ab ac ad
说明:
"${ids[@]}"
-使用shell数组的语法,无论是作为一部分echo
还是在此处使用。该@
部分的意思是“数组中的所有元素”tr ' ' '\n'
-将所有空格转换为换行符。因为您的数组被shell视为一行上的元素,并用空格分隔;并且因为sort期望输入在单独的行上。sort -u
-仅排序和保留唯一元素tr '\n' ' '
-将我们先前添加的换行符转换回空格。$(...)
-命令替换tr ' ' '\n' <<< "${ids[@]}"
是一种更有效的方法:echo "${ids[@]}" | tr ' ' '\n'
printf
这种方式(给出比格式字符串更多的参数)
sorted_unique_ids=($(echo "${ids[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))
。没有额外的括号,它以字符串形式给出。
... | uniq | ...
代替... | sort -u | ...
。
uniq
仅删除连续的重复项。在此答案的示例中,sorted_unique_ids
最终将与原始相同ids
。要保留顺序,请尝试... | awk '!seen[$0]++'
。另请参见stackoverflow.com/questions/1444406/…。
如果您运行的是Bash版本4或更高版本(在任何现代版本的Linux中都是这种情况),则可以通过创建一个包含原始数组的每个值的新关联数组,在bash中获得唯一的数组值。像这样:
$ a=(aa ac aa ad "ac ad")
$ declare -A b
$ for i in "${a[@]}"; do b["$i"]=1; done
$ printf '%s\n' "${!b[@]}"
ac ad
ac
aa
ad
之所以可行,是因为在任何数组(关联或传统,以任何语言)中,每个键只能出现一次。当for
循环到达aa
in的第二个值时a[2]
,它将覆盖b[aa]
最初为设置的值a[0]
。
与使用管道和外部工具(例如sort
和)相比,使用本机bash进行处理的速度可能更快uniq
,但是对于较大的数据集,如果使用诸如awk,python等更强大的语言,则可能会看到更好的性能。
如果您有信心,可以for
使用printf
的能力为多个参数回收其格式,从而避免循环,尽管这似乎是必需的eval
。(如果您满意,请立即停止阅读。)
$ eval b=( $(printf ' ["%s"]=1' "${a[@]}") )
$ declare -p b
declare -A b=(["ac ad"]="1" [ac]="1" [aa]="1" [ad]="1" )
该解决方案要求的原因eval
是数组值是在单词拆分之前确定的。这意味着命令替换的输出被视为单个单词,而不是一组键=值对。
尽管这使用了子外壳程序,但它仅使用bash内置函数来处理数组值。一定要用eval
肉眼评估您的使用情况。如果您不是100%相信chepner或glenn jackman或greycat不会发现您的代码有问题,请改用for循环。
我知道这个问题已经得到解答,但是它在搜索结果中的排名很高,可能会对某人有所帮助。
printf "%s\n" "${IDS[@]}" | sort -u
例:
~> IDS=( "aa" "ab" "aa" "ac" "aa" "ad" )
~> echo "${IDS[@]}"
aa ab aa ac aa ad
~>
~> printf "%s\n" "${IDS[@]}" | sort -u
aa
ab
ac
ad
~> UNIQ_IDS=($(printf "%s\n" "${IDS[@]}" | sort -u))
~> echo "${UNIQ_IDS[@]}"
aa ab ac ad
~>
ids=(ab "a a" ac aa ad ac aa);IFS=$'\n' ids2=(`printf "%s\n" "${ids[@]}" |sort -u`)
,所以我添加IFS=$'\n'
了@gniourf_gniourf的建议
IFS=$'\n'; ids2=(...)
,因为不可能在变量分配之前进行临时分配。而是使用以下构造:IFS=$'\n' read -r -a ids2 <<<"$(printf "%s\n" "${ids[@]}" | sort -u)"
。
如果您的数组元素有空格或任何其他shell特殊字符(您可以确定它们没有吗?),则首先要捕获这些字符(并且您应该始终这样做)用双引号将数组表示出来!例如"${a[@]}"
。Bash从字面上将其解释为“单独参数中的每个数组元素”。在bash中,这始终会始终有效。
然后,要获得排序(唯一)的数组,我们必须将其转换为sort可以理解的格式,并能够将其转换回bash数组元素。这是我想出的最好的方法:
eval a=($(printf "%q\n" "${a[@]}" | sort -u))
不幸的是,在空数组的特殊情况下,这失败了,将空数组变成了一个包含1个空元素的数组(因为printf的参数为0,但仍然打印时好像它的参数为一个空参数-参见说明)。因此,您必须抓住它,如果有的话。
说明:printf的%q格式“ shell”转义了打印的参数,就像bash可以以eval之类的方式恢复一样!由于每个元素都是在自己的行上进行转义的脱壳shell,因此元素之间的唯一分隔符是换行符,并且数组赋值将每行作为元素,将转义的值解析为文字文本。
例如
> a=("foo bar" baz)
> printf "%q\n" "${a[@]}"
'foo bar'
baz
> printf "%q\n"
''
eval对于删除转回数组的每个值是必需的。
uniq
代替sort -u
。
uniq
这在未排序的列表上无法正常工作,因此必须始终与结合使用sort
。
'sort'可用于订购for循环的输出:
for i in ${ids[@]}; do echo $i; done | sort
并使用“ -u”消除重复项:
for i in ${ids[@]}; do echo $i; done | sort -u
最后,您可以使用唯一元素覆盖数组:
ids=( `for i in ${ids[@]}; do echo $i; done | sort -u` )
ids=( `for i in ${ids[@]}; do echo $i; done | uniq` )
要创建一个由唯一值组成的新数组,请确保您的数组不为空,然后执行以下一项操作:
readarray -t NewArray < <(printf '%s\n' "${OriginalArray[@]}" | sort -u)
readarray -t NewArray < <(printf '%s\n' "${OriginalArray[@]}" | awk '!x[$0]++')
警告:请勿尝试执行NewArray=( $(printf '%s\n' "${OriginalArray[@]}" | sort -u) )
。它将在空间上断裂。
sort -u
改为uniq
。
uniq
仅合并相邻的重复行,因此与相同awk '!x[$0]++'
。
猫编号.txt
1 2 3 4 4 3 2 5 6
将行打印到列中:
cat number.txt | awk '{for(i=1;i<=NF;i++) print $i}'
1
2
3
4
4
3
2
5
6
找到重复的记录:
cat number.txt | awk '{for(i=1;i<=NF;i++) print $i}' |awk 'x[$0]++'
4
3
2
替换重复的记录:
cat number.txt | awk '{for(i=1;i<=NF;i++) print $i}' |awk '!x[$0]++'
1
2
3
4
5
6
仅查找Uniq记录:
cat number.txt | awk '{for(i=1;i<=NF;i++) print $i|"sort|uniq -u"}
1
5
6
如果要使用仅使用bash内部函数的解决方案,则可以将值设置为关联数组中的键,然后提取键:
declare -A uniqs
list=(foo bar bar "bar none")
for f in "${list[@]}"; do
uniqs["${f}"]=""
done
for thing in "${!uniqs[@]}"; do
echo "${thing}"
done
这将输出
bar
foo
bar none
处理嵌入式空白的另一种方法是用printf
,,进行分隔sort
,然后使用循环将其包装回数组中:
input=(a b c "$(printf "d\ne")" b c "$(printf "d\ne")")
output=()
while read -rd $'' element
do
output+=("$element")
done < <(printf "%s\0" "${input[@]}" | sort -uz)
最后,input
并output
包含所需的值(提供的顺序并不重要):
$ printf "%q\n" "${input[@]}"
a
b
c
$'d\ne'
b
c
$'d\ne'
$ printf "%q\n" "${output[@]}"
a
b
c
$'d\ne'
尝试此操作以获取文件中第一列的uniq值
awk -F, '{a[$1];}END{for (i in a)print i;}'
uniq=($(printf "%s\n" "${ids[@]}" | sort -u)); echo "${uniq[@]}"