使用 bash 脚本从句子中删除超过 [x] 个字符的单词

Using bash script to remove from sentence words longer than [x] characters

我有一个句子(数组),我想从中删除所有超过 8 个字符的单词。

例句:

var="one two three four giberish-giberish five giberish-giberish six"

我想得到:

var="one two three four five six"

到目前为止我正在使用这个:

echo $var | tr ' ' '\n' | awk 'length() <= 6 { print }' | tr '\n ' ' '

上面的解决方案工作正常,但如您所见,我将 space 替换为换行符,然后过滤单词,然后将换行符替换为 space。我很确定必须有更好更“优雅”的解决方案而不交换 space/newline.

这是一种方法:

arr=(one two three four giberish-giberish five giberish-giberish six)
for var in "${arr[@]}"; do (( ${#var} > 8 )) || echo -n "$var "; done
echo # for that newline in the end

还有一个:

awk '{ for(i=1;i<=NF;i++) { if(length($i) < 8) printf "%s ", $i } print "" # for that newline in the end }'

还有第三个!

awk -v RS='[[:space:]]+' 'length < 8 { v=v" "[=12=] }; END{print substr(v, 2)}'

最后一个打印一个“完美”的 single-space 分隔字符串,没有额外的前导或尾随空格。

你可以使用

#!/bin/bash
var="one two three four giberish-giberish five giberish-giberish six"
awk 'BEGIN{RS=ORS=" "} length([=10=]) <= 6' <<< "$var"
# -> one two three four five six

参见online demo

BEGIN{RS=ORS=" "} 将记录 input/output 分隔符设置为 space 并且 length([=14=]) <= 6 仅保留等于或小于 6 个字符的字段。

您还可以考虑使用 GNU sedperl 的解决方法:

sed -E 's/\s*\S{7,}//g' <<< "$var"
perl -pe 's/\s*\S{7,}//g' <<< "$var"

参见 this online demo

non-GNU sed 解决方法可能类似于

sed 's/[[:space:]]*[^[:space:]]\{7,\}//g' <<< "$var"

此处,所有出现零个或多个白色space(\s*[[:space:]]*)后跟七个或更多non-whitespace个字符(\S{7,}[^[:space:]]\{7,\}) 被删除。

使用sed

$ sed 's/\<[a-z-]\{8,\}\> //g' file
var="one two three four five six"

在纯 Bash 中,您可以将小于选定长度的单词过滤到一个新数组中:

#!/bin/bash

var="one two three four giberish-giberish five giberish-giberish six" 

new_arr=()
for w in $var; do  # no quotes on purpose to split string
    [[ ${#w} -lt 6 ]] && new_arr+=( "$w" )
done    

declare -p new_arr
# declare -a new_arr=([0]="one" [1]="two" [2]="three" [3]="four" [4]="five" [5]="six")

或者如果源已经是一个数组:

old_arr=(one two three four giberish-giberish five giberish-giberish six)
new_arr=()
for w in ${old_arr[@]}; do 
    [[ ${#w} -lt 6 ]] && new_arr+=( "$w" )
done 

您可能希望在遍历 old_arr 时删除其中的单词。如果你知道每个 $w 都是独一无二的,你可以这样做:

old_arr=(one two three four giberish-giberish five giberish-giberish six)
for w in ${old_arr[@]}; do 
    [[ ${#w} -ge 6 ]] && old_arr=("${old_arr[@]/$w}")
done 

但这有两个问题:1) 如果你有相同的前缀,所有将被删除和 2) 现有索引将保留:

$ declare -p old_arr
declare -a old_arr=([0]="one" [1]="two" [2]="three" [3]="four" [4]="" [5]="five" [6]="" [7]="six")

您还可以 unset 通过保留单独的索引来违规项目:

old_arr=(one two three four giberish-giberish five giberish-giberish six)
idx=0
for w in ${old_arr[@]}; do 
    [[ ${#w} -ge 6 ]] && unset 'old_arr[idx]'
    (( idx++ ))
done 

但是你最终会得到不连续的数组索引(但现有的限定词仍然在同一索引处):

$ declare -p old_arr
declare -a old_arr=([0]="one" [1]="two" [2]="three" [3]="four" [5]="five" [7]="six")

通常过滤到新数组中更好,除非您想保留现有索引。

这可能对你有用 (GNU sed):

<<<"$var" sed -E 'y/ /\n/;s/..{8}.*\n//mg;y/\n/ /'

将空格转换为换行符。

删除所有长度超过 8 个字符的行。

将换行符转换为空格。