如何根据字母顺序等在脚本/命令行中选择当前目录中文件的子范围?

Csa*_*nai 14 command-line bash zsh wildcards

在文件管理器中,您通常可以选择一个文件,然后按住Shift并选择另一个文件。中间的每个文件都将被选择。

我想做bash/zsh相当于这个。

即:我想给出 2 个文件名,并在中间包含每个文件名(按字母顺序排列 - 输出它们的方式ls)。

我知道? {} *和其他通配符选项,但是我希望能够在名称高度混乱的文件上使用它。

例如:给定文件

$ ls
aoeitoae.txt
oaeistn.txt
oaie.txt
paeoai.txt
sotaoe.txt
Run Code Online (Sandbox Code Playgroud)

我想发出这样的命令rm aoeitoae.txt-oaie.txt,然后得到:

$ ls
paeoai.txt
sotaoe.txt
Run Code Online (Sandbox Code Playgroud)

我怎样才能实现这个目标?

Gil*_*il' 10

这个答案主要针对 zsh。大部分工作不能在 bash 中轻松完成。

\n

许多常见情况都可以使用通配符来完成。尤其:

\n
    \n
  • 具有固定前缀的所有内容:foo-*
  • \n
  • 具有固定前缀的所有内容,后跟一个范围内的一个字母:(foo[b-m]*包括foobar, food, foomz1,但不包括fooarfoon
  • \n
  • 范围内的数字:(IMG-<7-42>.*包括IMG-8.PNGIMG-0042.JPG但不包括IMG-77.JPG
  • \n
\n

使用glob 限定符,有一种简单的方法来识别范围,但它需要计数foo*([1,3])匹配 列出的前 3 个文件foo*,无论它们是什么(如果少于 3 个,则匹配所有文件)。这发生在完成任何排序之后,例如foo*(om[1,3])匹配名称以foo.

\n

你可以让 zsh 帮你算出数字。分两步完成:首先将所有匹配项放入数组中,然后使用下标标志 i查找端点,并且Ie如果您想防止任何通配符匹配):是数组中从元素到元素$a[$a[(i)foo],$a[(I)bar]]的部分,如果或不存在则为空。$afoobarfoobar

\n
a=(*.txt(oL))\n# List the files that appear between small.txt and large.txt in a listing by size.\n# Note that files that have the same size as one of the bounds may or may not be included.\necho $a[$a[(I)small.txt],$a[(I)large.txt]]\n
Run Code Online (Sandbox Code Playgroud)\n

所以这里的函数完全实现了问题的要求(除了精确的语法,这是无法完成的):

\n
# Usage: select_range FROM TO WILDCARD_PATTERN\n# Sets the array $s to the files matching PATTERN from FROM to TO inclusive.\nfunction select_range {\n  if (($# < 2)); then\n    echo >&2 "select_range: missing range arguments"\n    return 120\n  fi\n  local from=$1 to=$2\n  shift 2\n  from=$@[(ie)$from]\n  if ((from == 0)); then\n    echo >&2 "select_range: not matched: $from"\n  fi\n  to=$@[(Ie)$to]\n  if ((to == 0)); then\n    echo >&2 "select_range: not matched: $from"\n  fi\n  s=($@[$from,$to])\n}\n
Run Code Online (Sandbox Code Playgroud)\n

用法:select_range aoeitoae.txt oaie.txt * && rm $s

\n

globe限定符允许您编写任意代码来过滤结果,但它已经开始变得有点笨拙。在复杂的情况下,引用可能会很棘手;为了简单起见,请使用\'作为分隔符(需要用反斜杠引用)并将过滤器代码放在单引号中,这意味着模式如下所示:foo-*(e\\\'\'code goes here\'\\\')。(如果引用变得太复杂,请编写一个函数并使用限定符。)按字典顺序+过滤前后的aoeitoae.txt文件:。oaie.txt *(e\\\'\'! [[ $REPLY < aoeitoae.txt || $REPLY > oaie.txt ]]\'\\\')

\n

请注意,过滤器中进行的比较不一定使用与通配符扩展相同的顺序。例如,foo-*(n)列表foo-9之前foo-10得益于限定符n,但[[ foo-9 > foo-10 ]]在字符串比较中,并没有类似于对整数子字符串进行数字比较的条件运算符。>如果要与按数字排序的整数部分进行字符串比较,可以使用n 参数扩展标志进行数组排序,并检查它是否将匹配的名称保留在中间:*(ne\\\'\'a=(b11r $REPLY f10o); [[ $a[2] == "${${(@n)a}[2]}" ]]\'\\\'))包括b101r, b11s, d1, f02o, \xe2\x80\xa6,但不包括b9r, f011, \xe2\x80\xa6

\n

如果您按日期匹配文件,则可以使用-nt条件(请注意,文件不比其自身更新):*(ome\\\'\'! [[ $REPLY -ot from || $REPLY -nt to ]]\'\\\')仅包含在的修改时间和 的修改时间(含)之间修改的文件fromto

\n


Fre*_*ddy 8

使用 bash 函数:

sfiles ()
(
    # run this in a subshell, so we don't have to care if nullglob/dotglob were enabled or not
    [ $# -eq 0 ] && exit
    
    local nullsep=0
    if [ "$1" = "-0" ]; then
        nullsep=1; shift
    fi
    local first=$1
    shift $(($# -1))
    local last=$1
    local files=( )

    shopt -s nullglob dotglob
    for i in *; do
        # first argument found or array not empty?
        if [ "$i" = "$first" ] || [ "${#files[@]}" -ne 0 ]; then
            files+=( "$i" )
        fi
        # last argument found? break loop
        [ "$i" = "$last" ] && break
    done

    if [ "${#files[@]}" -gt 0 ]; then
        [ "$nullsep" -eq 1 ] && 
            printf '%s\0' "${files[@]}" ||
            printf '%s\n' "${files[@]@Q}"
    fi
)
Run Code Online (Sandbox Code Playgroud)

它输出第一个参数和最后一个参数(包括)之间的所有文件。

例子:

$ ls -A
 btjhyyxrlv.txt    otewagahzp.txt       .xxx
 crlcsbzizl.txt    ssffszhdmp.txt      'zdjtgahx q.txt'
 hgiagchkgt.txt   'tt'$'\t''aa.txt'    'zmwik zhur.txt'
 jusupbivit.txt    umikyfucgu.txt      'z otmleqlq.txt'
' kcyigyurc.txt'  ' upvpntdfv.txt'      .zzz
 kfthnpgrxm.txt   'uu'$'\t\t''aa.txt'
 lgzsmquxwj.txt    wlwexgzohs.txt
Run Code Online (Sandbox Code Playgroud)
$ sfiles c* k*
'crlcsbzizl.txt'
'hgiagchkgt.txt'
'jusupbivit.txt'
' kcyigyurc.txt'
'kfthnpgrxm.txt'
Run Code Online (Sandbox Code Playgroud)
$ sfiles .xxx .zzz
'.xxx'
'zdjtgahx q.txt'
'zmwik zhur.txt'
'z otmleqlq.txt'
'.zzz'
Run Code Online (Sandbox Code Playgroud)
$ LC_ALL=C sfiles .xxx .zzz
'.xxx'
'.zzz'
Run Code Online (Sandbox Code Playgroud)

顺序错误,这个什么也不返回:

$ sfiles .zzz .xxx
Run Code Online (Sandbox Code Playgroud)

使用以下命令删除选定的文件xargs

$ sfiles .xxx .zzz | xargs rm
Run Code Online (Sandbox Code Playgroud)

对于带有制表符或换行符的文件名,添加选项-0作为第一个参数,以进行空分隔输出,无需 bash 引用。

$ sfiles -0 tt* uu* | xargs -0 ls
'tt'$'\t''aa.txt'  ' upvpntdfv.txt'
 umikyfucgu.txt    'uu'$'\t\t''aa.txt'
Run Code Online (Sandbox Code Playgroud)