如何通过部分名称选择一系列文件

如何通过部分名称选择一系列文件

我想通过使用文件名的一部分来选择一系列文件,以用于复制、移动等。

随机示例列表:

  1. α1a
  2. α2aa
  3. 阿尔法3abc
  4. 布拉沃A1
  5. 布拉沃B2bb
  6. 查理
  7. DeltaA1FDFD
  8. DeltaA2大法
  9. 德尔塔A2222
  10. δC1
  11. ...(一堆其他文件)

我想运行一些从 alpha2* 到 deltaA* 的命令,这将在列表中选择 2-9。这样我就可以根据名称选择一个范围,而不用担心实际有多少文件,并且不会获得额外的内容。

答案1

start=alpha2*
end=deltaA*

for file in *
do
    if [[ $file > $start && ( $file < $end || $file == $end ) ]]; then
        echo $file
    fi
done

您可以将文件存储在数组中,而不是 echo:

array+=($file)

然后使用数组来复制、移动文件...或者简单地在 for 循环中执行命令。

答案2

Shell 编程不太擅长字典顺序比较。在 bash、ksh 或 zsh 中,您可以使用< 条件运算符比较两个字符串(POSIX sh 仅具有数字比较)。

run_in_range () {
  start_at="$1"; end_before="$2"; command="$3"; shift 3
  for item do
    if [[ ! ($x < $start_at) && ($x < $end_before) ]]; then
      "$command" "$item"
    fi
  done
}
run_in_range alpha2 deltaB some_command *

请注意,我使用第一个排除的项目作为第二个参数:deltaB是所有以 开头的字符串之后的最小字符串deltaA。如果你想deltaA作为参数传递,你可以这样做。

run_in_inclusive_range () {
  start_at="$1"; end_on_prefix="$2"; command="$3"; shift 3
  for item do
    if [[ ! ($x < $start_at) && ($x < $end_on_prefix || $x == "$end_on_prefix"*) ]]; then
      "$command" "$item"
    fi
  done
}
run_in_inclusive_range alpha2 deltaA some_command *

此代码不依赖于通配符的顺序:即使项目列表未排序,它也可以工作。这使得代码更加健壮,但速度稍慢,因为它必须遍历整个列表。

这是一种仅依赖于 POSIX 功能的替代方法。它用于sort进行字符串比较。这假设项目中没有换行符。此函数恢复 和 globbing 的默认设置IFS(恢复环境设置是可能的,但令人烦恼的是很难)。

run_in_range () {
  IFS='
'; set -f
  start_at="$1"; end_before="$2"; command="$3"; shift 3
  set $({
          printf '%s1\n' "$@"
          printf '%s0\n' "$start_at" "$end_before"
        } | sort | sed '/0$/,/0$/ s/1$//p')
  unset IFS; set +f
  for item do
    "$command" "$item"
  done
}

解释:

  1. 构建一个包含附加项目1和附加边界的列表0
  2. 对列表进行排序。
  3. 打印两条边界线之间的线(以 结尾的线0),但仅打印以 结尾的线1(即项目),并且不带尾随1

答案3

在我看来,这个问题比看起来更容易解决:

假设有一种方法可以获取列表,每行一个文件。
对于当前目录:

list="$(printf '%s\n' *)"

只需将限制附加到列表中并对其进行排序:

nl='
'
lim1="alpha2"
lim2="deltaA"

echo -- "$list""$nl""$lim1""$nl""$lim2" | sort

然后开始在 lim1 上打印(注意 lim1 和 lim2 与现有文件名不匹配)并在 lim2 上停止。

另外,为了简单起见,请选择要打印的文件之前的最短字符串以及需要选择的最后一个文件之后排序的最短字符串。

有用的是一个排他列表(不包括限制的列表),
但提供了两种解决方案。

基本价值观:

list="$(printf '%s\n' *)"

lim1=alpha2
lim2=deltaA

使用 awk 获得包含列表的解决方案:

echo "awk----------------inclusive"
echo "$list""$nl""$lim1""$nl""$lim2" | sort |
    awk -v lim1="$lim1" -v lim2="$lim2" '$0==lim1,$0==lim2{print}'

使用 awk 获取独占列表的解决方案:

echo "awk----------------exclusive"
echo "$list""$nl""$lim1""$nl""$lim2" | sort |
    awk -v lim1="$lim1" -v lim2="$lim2" '$0==lim1{p=1;next};$0==lim2{p=0};p'

带有包含列表的 shell 的解决方案:

echo "shell---------------inclusive"
show=0
echo "$list""$nl""$lim1""$nl""$lim2" | sort |
    while IFS="$nl" read -r line; do
        [ "$line" = "$lim1" ] && show=1
        [ "$show" = "1"     ] && printf '%s\n' "$line"
        [ "$line" = "$lim2" ] && show=0
    done

以及外壳的独家解决方案:

show=0
echo "$list""$nl""$lim1""$nl""$lim2" | sort |
    while read -r line; do
        [ "$line" = "$lim2" ] && show=0
        [ "$show" = "1"     ] && printf '%s\n' "$line"
        [ "$line" = "$lim1" ] && show=1
    done

相关内容