bash:展开参数列表并将其传递给 find - escaping/white spaces hell
bash: Expand list of arguments and pass it to find - escaping/white spaces hell
我想检查文件夹中的文件并删除其中的一些文件。一个条件是保留某种类型的所有文件(例如 .txt),并保留所有具有第一次搜索的名称但具有不同扩展名的文件([第一次搜索的名称]. ).应删除目录中的所有其他文件。
这可以通过 find . -type f -not -name xxx
命令轻松实现。但是,我想为每个自动找到的 [name of first search] 填充 find 命令。
为此我写了这个小脚本
#!/bin/bash
while read filename; do
filename=$(echo $filename | sed 's/\ /\\ /g')
filename=\'$filename*\'
file_list=$file_list" -not -name $filename"
done <<<"$(ls *.txt | sed 's/.txt//g')"
find . -type f $file_list -print0| while read -d $'[=12=]' FILE
do
rm -f "$FILE"
done
$file_list 很好地填充了相应的数据,但是,查找失败说:
find: unknown predicate `-\'
如果我使用 sed 命令 (' ' -> '\ ') 或
find: paths must precede expression: - Usage: find [-H] [-L] [-P]
[-Olevel] [-D [help|tree|search|stat|rates|opt|exec] [path...]
[expression]
如果我评论 sed 行。
bash -x 显示了以下执行的命令:
没有 sed 命令:
find . -type f -not -name ''\''Text' - here - or - there*'\'''
使用 sed 命令:
find . -type f -not -name ''\''Text\' '-\' 'here\' '-\' 'or\'
'there*'\'''
这甚至可以通过 find 实现吗?我也尝试在 find 命令中转义 $find_list
但没有成功。
试试这个
#!/bin/bash
remove_except()
{
local extension=$( printf "%q" "" )
local dir=$( printf "%q" "" )
local start_dir=$(pwd)
[ -z "$extension" ] && return 1
[ -z "$dir" ] || [ ! -d "$dir" ] && dir="."
cd "$dir"
local this="[=10=]"
this="${this##*/}"
# exclude myself and extension
local excludes=" -name \"$this\" -o -name \"*.$extension\" "
for f in *."$extension";
do
filename="${f%.*}"
excludes="$excludes -o -name \"$filename.*\""
done
eval "find . -maxdepth 1 -type f -not \( $excludes \) -print0" | xargs -0 -I {} rm -v {}
cd "$start_dir"
}
remove_except "txt" "/your/dir"
放入脚本,例如remove_except.sh
和 运行 是这样的:
remove_except.sh "txt" "/your/dir"
第二个参数是可选的,如果未指定,将假定为 .
。
使用数组,而不是字符串。
#!/bin/bash
# ^-- must be /bin/bash, not /bin/sh, for this to work
excludes=( )
for filename in *.txt; do
excludes+=( -not -name "${filename%.txt}" )
done
find . -type f -not -name '*.txt' "${excludes[@]}" -exec rm -f '{}' +
要了解其工作原理,请参阅 BashFAQ #50。
现在,如果你想与 /bin/sh
兼容,而不仅仅是 bash,那么将其封装在一个函数中,这样你就可以覆盖参数列表(这是唯一可用的数组)而无需丢弃脚本的全局参数:
delete_except_textfiles() {
local filename 2>/dev/null ||: "local keyword not in POSIX, ignore if not present"
set --
for filename in *.txt; do
set -- "$@" -not -name "${filename%.txt}"
done
find . -type f -not -name '*.txt' "$@" -exec rm -f '{}' +
}
delete_except_textfiles
我想检查文件夹中的文件并删除其中的一些文件。一个条件是保留某种类型的所有文件(例如 .txt),并保留所有具有第一次搜索的名称但具有不同扩展名的文件([第一次搜索的名称]. ).应删除目录中的所有其他文件。
这可以通过 find . -type f -not -name xxx
命令轻松实现。但是,我想为每个自动找到的 [name of first search] 填充 find 命令。
为此我写了这个小脚本
#!/bin/bash
while read filename; do
filename=$(echo $filename | sed 's/\ /\\ /g')
filename=\'$filename*\'
file_list=$file_list" -not -name $filename"
done <<<"$(ls *.txt | sed 's/.txt//g')"
find . -type f $file_list -print0| while read -d $'[=12=]' FILE
do
rm -f "$FILE"
done
$file_list 很好地填充了相应的数据,但是,查找失败说:
find: unknown predicate `-\'
如果我使用 sed 命令 (' ' -> '\ ') 或
find: paths must precede expression: - Usage: find [-H] [-L] [-P] [-Olevel] [-D [help|tree|search|stat|rates|opt|exec] [path...] [expression]
如果我评论 sed 行。
bash -x 显示了以下执行的命令:
没有 sed 命令:
find . -type f -not -name ''\''Text' - here - or - there*'\'''
使用 sed 命令:
find . -type f -not -name ''\''Text\' '-\' 'here\' '-\' 'or\' 'there*'\'''
这甚至可以通过 find 实现吗?我也尝试在 find 命令中转义 $find_list
但没有成功。
试试这个
#!/bin/bash
remove_except()
{
local extension=$( printf "%q" "" )
local dir=$( printf "%q" "" )
local start_dir=$(pwd)
[ -z "$extension" ] && return 1
[ -z "$dir" ] || [ ! -d "$dir" ] && dir="."
cd "$dir"
local this="[=10=]"
this="${this##*/}"
# exclude myself and extension
local excludes=" -name \"$this\" -o -name \"*.$extension\" "
for f in *."$extension";
do
filename="${f%.*}"
excludes="$excludes -o -name \"$filename.*\""
done
eval "find . -maxdepth 1 -type f -not \( $excludes \) -print0" | xargs -0 -I {} rm -v {}
cd "$start_dir"
}
remove_except "txt" "/your/dir"
放入脚本,例如remove_except.sh
和 运行 是这样的:
remove_except.sh "txt" "/your/dir"
第二个参数是可选的,如果未指定,将假定为 .
。
使用数组,而不是字符串。
#!/bin/bash
# ^-- must be /bin/bash, not /bin/sh, for this to work
excludes=( )
for filename in *.txt; do
excludes+=( -not -name "${filename%.txt}" )
done
find . -type f -not -name '*.txt' "${excludes[@]}" -exec rm -f '{}' +
要了解其工作原理,请参阅 BashFAQ #50。
现在,如果你想与 /bin/sh
兼容,而不仅仅是 bash,那么将其封装在一个函数中,这样你就可以覆盖参数列表(这是唯一可用的数组)而无需丢弃脚本的全局参数:
delete_except_textfiles() {
local filename 2>/dev/null ||: "local keyword not in POSIX, ignore if not present"
set --
for filename in *.txt; do
set -- "$@" -not -name "${filename%.txt}"
done
find . -type f -not -name '*.txt' "$@" -exec rm -f '{}' +
}
delete_except_textfiles