Bash ls 具有双重替换

Bash ls 具有双重替换

我正在编写一个 bash 脚本,该脚本从命令行参数读取目录并ls对其执行操作。

如果我指定一个简单的目录,那么一切都可以。

但我想做类似的事情/home/{x,y},所以脚本的调用是my-script -d '/home/{x,y}' 的值'/home/{x,y}'存储在名为 的变量中source

  • 我尝试过ls "${source}",但它返回

    ls: cannot access /home/{x,y}: No such file or directory
    
  • 我尝试过ls ${!source},但它返回当前目录中的文件。

  • 我尝试过ls \$$source,但再次失败并出现第一个错误。

如何扩展'/home/{x,y}'脚本中的参数以便它运行ls /home/x /home/y

答案1

通常的方法是将目录作为主命令行参数传递,而不需要选项标志。即用户将运行my-script -a -b foo /home/me/dir1 /some/other/path.运行解析选项后getopts,您将保留位置参数中的目录,并且可以运行ls "$@".在这里,用户可以使用 shell 允许他们填充的任何扩展来填充最后的参数,无论是/some/path/{foo,bar},还是/some/path/*

或者,让用户传递多个-d选项,以便他们运行my-script -a -b foo -d /home/me/dir1 -d /some/other/path.在这里,使用扩展比较困难,因为他们需要-d在每个参数前面添加 shell 允许的任何内容。也许有类似的东西a=(/path/{this,that}); my-script -a -b foo "${a[@]/#/-d}"

无论如何,在脚本中复制 shell 的扩展并不常见,而且可能会让用户措手不及,因此我建议不要这样做。如果您这样做,您还需要决定哪个扩展以支持。并非所有 shell 都支持所有扩展,也并非所有用户都知道每个 shell 支持的所有扩展。

答案2

示例.sh:

#!/usr/bin/env bash
FILE_PATTERN="$1"
MATCHING_FILES=$(eval "echo $FILE_PATTERN")
echo "Files: $MATCHING_FILES"
ls -l $MATCHING_FILES

这将使用 example.sh 的第一个参数作为 shell glob 模式。它可能会破坏包含空格或特殊字符的文件名。如何引用变量以避免这种情况,同时仍然应用该模式有待您找出。

用法:

$ ./example.sh 'file-{x,y}.txt'
Files: file-x.txt file-y.txt
-rw-r--r--  1 user  group  0 30 Sep 16:47 file-x.txt
-rw-r--r--  1 user  group  0 30 Sep 16:47 file-y.txt

如果可以的话,最好是不是执行此操作并接受多个文件名作为参数。它肯定会使变量引用更加清晰和容易,并且您可以避免处理 Stéphane 提出的关于看起来像 glob 模式的文件名的问题。

答案3

我会做:

#! /bin/bash -
sources=()
while getopts d:... o; do
  case $o in
    (d) sources+=("$OPTARG");;
    (...) ;;
  esac
done
shift "$((OPTIND - 1))"

ls -- "${sources[@]}"

也就是说,让您的脚本获取目录列表,其中调用者-d对每个目录使用一个目录。

然后用户可以执行以下操作:

my-script -d/home/{x,y}

如果他们的 shell 支持大括号扩展,或者他们的 shell 提供了任何有助于构建该列表的功能。

rc//中fishzsh -o rcexpandparam可以是:

my-script -d$array

例如(也-d$^array没有rcexpandparamin zsh

zsh

my-script /home/*(P[-d])

ETC。

为了让您的脚本对参数执行大括号扩展而不是其他形式的扩展,您可以引用除{, }and之外的所有字符序列,.0-9如果您想允许{1..10}样式字符),然后运行它eval

zsh使用or会更容易,ksh93因为bash's${var//pattern/replacement}非常有限:

#! /bin/zsh -
source=${1?}

set -o extendedglob
eval "sources=( ${source//(#m)[^"{}".,0-9]##/'$MATCH'} )"

printf ' - "%s"\n' "$sources[@]"

例子:

$ myscript 'x\{1..3}$$`{a b,c}'
 - "x\1$$`a b"
 - "x\1$$`c"
 - "x\2$$`a b"
 - "x\2$$`c"
 - "x\3$$`a b"
 - "x\3$$`c"

但随后,用户将无法再传递文字foo{a,b}参数。

答案4

发生这种情况是因为它被评估为多个参数而不是一个。尝试这个:

ls "$@"

$@将所有参数扩展为空格分隔的字符串。

相关内容