【问题标题】:How do shell remove the first file/folder of list pattern with pairs of almost identical nameshell如何删除具有几乎相同名称对的列表模式的第一个文件/文件夹
【发布时间】:2020-10-20 14:58:02
【问题描述】:
Bash shell 如何删除第一个 ie。列表模式的早期文件/文件夹,具有几乎相同的名称对,如插图;
(真实的不是直接来自 ls 而是来自管道)
$ ls
aaa
aab
bbb
bbc
mmm
mmn
xxx
xxy
只是说明:ls | R M ...? ...终于得到了
$ ls
aaa
bbb
mmm
xxx
怎么样?
【问题讨论】:
标签:
bash
sed
grep
filesystems
batch-processing
【解决方案1】:
这是 bash 的部分内容。它使用此处提供的 Levenshtein 距离实现:https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#Bash 用于计算连续 dir 条目之间的编辑距离,并通过设置适当的阈值,如果它与前一个条目足够接近,则可以修改它以删除后一个条目(因为它不会'不要删除任何东西)。
这当然会导致问题或疑问,例如,如果您有文件 aaa、bbb 和 aac 或 caa。 aac 和 caa 与 aaa 相比的编辑距离是相同的,但文件 bbb 的存在会改变删除行为。尝试一下,它不会按原样删除任何内容,而且它可能对您有用或提出您没有想到的要求。
#!/bin/bash
function levenshtein {
if [ "$#" -ne "2" ]; then
echo "Usage: $0 word1 word2" >&2
elif [ "${#1}" -lt "${#2}" ]; then
levenshtein "$2" "$1"
else
local str1len=$((${#1}))
local str2len=$((${#2}))
local d i j
for i in $(seq 0 $(((str1len+1)*(str2len+1)))); do
d[i]=0
done
for i in $(seq 0 $((str1len))); do
d[$((i+0*str1len))]=$i
done
for j in $(seq 0 $((str2len))); do
d[$((0+j*(str1len+1)))]=$j
done
for j in $(seq 1 $((str2len))); do
for i in $(seq 1 $((str1len))); do
[ "${1:i-1:1}" = "${2:j-1:1}" ] && local cost=0 || local cost=1
local del=$((d[(i-1)+str1len*j]+1))
local ins=$((d[i+str1len*(j-1)]+1))
local alt=$((d[(i-1)+str1len*(j-1)]+cost))
d[i+str1len*j]=$(echo -e "$del\n$ins\n$alt" | sort -n | head -1)
done
done
echo ${d[str1len+str1len*(str2len)]}
fi
}
for f in "$PWD/"*
do
ret=$(levenshtein "$p" "$f")
if [[ ret -le 1 ]] # threshold here is 1 edit
then
echo rm "$f"
else
echo leave "$f"
fi
p="$f" # store current entry for next round
done
这是一个示例运行的输出:
$ cd /home/james/test
$ bash ../test.sh
leave /home/james/test/aaa
rm /home/james/test/aab
leave /home/james/test/bbb
rm /home/james/test/bbc
【解决方案2】:
我的理解是:您希望从您的列表中删除以下内容:
- “几乎相同名称的对”:从您的示例来看,当第一个字符相同时,这是正确的
这是一个适用于 bash 的单行代码:
ls | while read -r txt; do printf "% 40s:" "${txt:1}"; echo ${txt:0:1}; done | uniq --skip-chars 41 | sed -e 's/ *\(\S*\):\(.\)/\2\1/'
编辑:我首先误解了 OP 想要删除被认为具有“几乎相同的名称”的文件/文件夹。这是我更正的单行“
ls | tee >(uniq --check-chars 1) | sort | uniq -u | xargs rm -v -d
【解决方案3】:
这可能对你有用(GNU sed):
sed -En 'N;/^((.*).)\n\2.$/s//rm \1/p' file
一次读取两行文件。
如果第一个文件/行除了最后一个字符之外与后面的文件/行相同,请创建一个脚本将其删除。
研究输出,如果满足要求,则:
sed -En 'N;/^((.*).)\n\2.$/s//rm \1/p' file | sh
或:
sed -En 'N;/^((.*).)\n\2.$/s//rm \1/e' file