如何将 argv 与通配符一起使用?

How do I use argv with a wildcard?

我正在尝试编写一个函数来更改当前目录中所有匹配文件的扩展名:

function chext
  if count $args -eq 2 > /dev/null
    for f in (ls *$argv[1])
      mv (basename $f $argv[2]) (basename $f $argv[1])
    end
  else
    echo "Error: use the following syntax"
    echo "chext oldext newext"
  end
end

虽然我一直得到这个输出:

(*master) λ chext markdown txt
No matches for wildcard '*$argv[1]'.  (Tip: empty matches are allowed in 'set', 'count', 'for'.)
~/.config/fish/config.fish (line 1): ls *$argv[1]
                                        ^
in command substitution
        called on line 60 of file ~/.config/fish/config.fish

in function 'chext'
        called on standard input
        with parameter list 'markdown txt'

我知道必须有一种方法可以对变量进行插值并将 * 保留为通配符,但我无法弄清楚。我试过使用 (string join '*' $argv[1]),但它把通配符变成了字符串。

我确实做到了 几乎 工作:

function chext
  if count $args -eq 2 > /dev/null
    for f in (ls (string join * $argv[1]))
      mv (basename $f $argv[2]) (basename $f $argv[1])
    end
  else
    echo "Error: use the following syntax"
    echo "chext oldext newext"
  end
end

它删除了我所有文件的扩展名,但没有添加新的,然后给我一个错误,这是所有文件名的组合,并说文件名太长。

更新:

我确定有一种方法可以让 ls 正常工作,但我确实让这个工作成功了:

if count $args -eq 2 > /dev/null
    for f in (ls)
      mv $f (string replace -r \.$argv[1]$ .$argv[2] (basename $f))
    end
  else
    echo "Error: use the following syntax"
    echo "chext oldext newext"
  end
end

答案在留言中:

No matches for wildcard '*$argv[1]'. (Tip: empty matches are allowed in 'set', 'count', 'for'.)

这意味着

for i in *argv[1]

有效,

set -l expanded *argv[1]

如果您尝试使用不匹配的 glob 启动任何其他命令,fish 将跳过执行并打印错误。


您的功能还有很多其他问题,请允许我一一解决。

if count $args -eq 2 > /dev/null

count 不接受“-eq 2”参数。只要给它一个参数,它就会 return 0(即 true),所以这将永远是 true。

您尝试做的是 if test (count $args) -eq 2

同样有效的(也是我碰巧更喜欢的)是 if set -q argv[2]

(另外,它是 "argv",而不是 "args")。

for f in (ls *$argv[1])

请不要使用ls的输出。它在 fish 中的问题没有在 bash 中那么多(一旦文件名称中包含 space、制表符或换行符,它就会中断),但这是不必要的(它启动一个额外的过程)并且仍然有一个问题 - 当文件名称中有换行符时它会中断。

for f in *$argv[1]也能很好的解决你的问题

 mv (basename $f $argv[2]) (basename $f $argv[1])

我不确定您在这里使用的 basename 是什么,但我的只是 删除了 一个后缀。因此,如果您执行 chext .opus .ogg,这将执行

 mv (basename song.opus .ogg) (basename song.opus .opus)

这将解析为

 mv song.opus song

就个人而言,我会为此使用 string replace。像

mv $f (string replace -- $argv[1] $argv[2] $f)

(注意:如果扩展字符串出现在前面,这会做错事,例如,如果你有一个名为 "f.ogg-banana.ogg" 的文件,只有第一个“.ogg”会被改变。你可能想使用常规表达式:string replace -r -- "$argv[1]$" $argv[2] $f)