如何将 ms-dos 风格的通配符与 ls 和 mv 一起使用?

如何将 ms-dos 风格的通配符与 ls 和 mv 一起使用?

我很不幸拥有 MS-DOS 背景 - 但至少它让我体会到 Linux 的强大程度。我一直在努力让我的 Linux-Fu 达到标准,但是有一些可以用 DOS 完成的事情我不确定如何完成最容易对于Linux:

重命名多个文件 - 使用两个通配符

c:\> dir

Directory of c:\
    file1.txt
    file2.txt
    file3.txt
    file4.txt

c:\>rename *.txt *.bak

c:\> dir

Directory of c:\
    file1.bak
    file2.bak
    file3.bak
    file4.bak

我知道我可以find -exec在这里使用,但可以使用更短的语法 - 也许mv带有一些特殊的标志或语法?我想这的关键是第二 *通配符作为linux不应该对第一个有问题(即我知道如何选择我想使用通配符重命名的文件)

重命名单个文件 - 使用一个通配符

c:\> dir

Directory of c:\
    file1.txt

c:\>rename file1.txt *.bak

c:\> dir

Directory of c:\
    file1.bak

当重命名又长又笨重的文件名时,这将特别有用。我想也许我可以用它mv file1.txt $1.bak来结束file1.txt.bak也可以接受,但我不确定您是否可以引用$1与 shell 命令内联的参数。同样,在这种特殊情况下,ms-dos 如何将*通配符混为一谈,将其用作文件名部分的捕获/替换匹配,这很方便。

使用通配符过滤目录列表

c:\> dir

Directory of c:\
    file1.txt
    file2.txt
    file3.txt
    file4.txt
    text.txt
    \temp       (directory) 

c:\> dir file*

Directory of c:\
    file1.txt
    file2.txt
    file3.txt
    file4.txt

c:\> t*

Directory of c:\
    text.txt
    \temp       (directory) 

我不确定这样做的正确语法ls是什么,或者是否可能。如果我做了类似的事情,ls t*它将递归到以t.我的解决方法是使用find . --max-depth 1 -iname "t*"或类似的东西ls -al | grep t- 这两种方法都不像dir t*现在那么短和简单。

最后,我知道我可以设置别名来缩短这些长命令,但我想学习一些开箱即用的 linux-fu 来完成这些事情,因为有时你会连接到远程系统或工作在新机器上。

那么我怎样才能像以前一样使用文件mv操作呢?lsdirrename

答案1

Windows 和 POSIX shell 之间的根本区别之一cmd是谁负责通配符扩展。 Shell 在启动您要求的实际命令之前执行所有所需的扩展。cmd 大多将通配符模式不加修改地传递给命令。(我说大多数,因为我认为有例外,并且环境变量在大多数情况下都会被扩展。)这使得编写与 中rename相同的语法一起工作的程序变得cmd相当棘手。

但是有一个rename针对 Linux 的 - 具有完全不同的参数,请查看手册页(在我的系统上有点简洁,并且rename来自util-linux我系统上的软件包,应该广泛使用)。您的第一次重命名将像这样完成:

rename .txt .bak *.txt

请注意,shell 执行了*扩展,因此rename它本身实际上认为它是这样调用的:

rename .txt .bak file1.txt file2.txt file3.txt ...

所以你可以猜测单个文件版本:

rename .txt .bak file1.txt

如果您不想使用rename而是自己实现它,您可以为此创建一个函数。假设您只想更改文件扩展名,对于单个文件重命名,请查看以下内容:

$ function chext() {
  newext="$1"
  file="$2"
  newfile="${file%.*}$newext"
  echo mv "$file" "$newfile"
}
$ chext .csv test.txt
mv text.txt text.csv

$newfile是使用一个构建的子串删除删除原始扩展名,然后连接新扩展名。您可以扩展该功能以相对轻松地处理多个文件。


至于你的ls问题,请使用-d开关。这将阻止ls列出目录的内容。

演示:

$ ls -al
total 536
drwx------   3 owner users 528384 Jan  7 17:29 .
drwxr-xr-x 126 owner users  12288 Jan  7 17:26 ..
-rw-r--r--   1 owner users      0 Jan  7 17:28 f1.csv
-rw-r--r--   1 owner users      0 Jan  7 17:28 f2.csv
-rw-r--r--   1 owner users      0 Jan  7 17:28 f3.csv
-rw-r--r--   1 owner users      0 Jan  7 17:28 f4.csv
drwxr-xr-x   2 owner users   4096 Jan  7 17:33 test
-rw-r--r--   1 owner users      0 Jan  7 17:27 test.csv

通配符重命名

$ rename .csv .txt f*
$ ls -al
total 536
drwx------   3 owner users 528384 Jan  7 17:34 .
drwxr-xr-x 126 owner users  12288 Jan  7 17:26 ..
-rw-r--r--   1 owner users      0 Jan  7 17:28 f1.txt
-rw-r--r--   1 owner users      0 Jan  7 17:28 f2.txt
-rw-r--r--   1 owner users      0 Jan  7 17:28 f3.txt
-rw-r--r--   1 owner users      0 Jan  7 17:28 f4.txt
drwxr-xr-x   2 owner users   4096 Jan  7 17:33 test
-rw-r--r--   1 owner users      0 Jan  7 17:27 test.csv

单个文件重命名

$ rename .txt .csv f1.txt 
$ ls -al
total 536
drwx------   3 owner users 528384 Jan  7 17:34 .
drwxr-xr-x 126 owner users  12288 Jan  7 17:26 ..
-rw-r--r--   1 owner users      0 Jan  7 17:28 f1.csv
-rw-r--r--   1 owner users      0 Jan  7 17:28 f2.txt
-rw-r--r--   1 owner users      0 Jan  7 17:28 f3.txt
-rw-r--r--   1 owner users      0 Jan  7 17:28 f4.txt
drwxr-xr-x   2 owner users   4096 Jan  7 17:33 test
-rw-r--r--   1 owner users      0 Jan  7 17:27 test.csv

默认ls

$ ls -l t*
-rw-r--r-- 1 owner users    0 Jan  7 17:27 test.csv

test:
total 0
-rw-r--r-- 1 owner users 0 Jan  7 17:33 dont_show_me_please

ls不检查目录

$ ls -ld t*
drwxr-xr-x 2 owner users 4096 Jan  7 17:33 test
-rw-r--r-- 1 owner users    0 Jan  7 17:27 test.csv

答案2

谈到通配符时要记住的一件事是它们是由 shell 扩展的。应用程序不知道您是否使用了通配符或键入了名称。例如,如果您键入rename *.txt *.bak,则该rename命令会看到类似 的内容rename file1.txt file2.txt existingfile.bak。这些信息还不足以继续下去。

我先解决这个问题ls,因为它更简单。如果您想要的只是匹配的名称,那么您不需要ls,因为 shell 已经在进行扩展。

echo t*

如果您想要有关文件的更多信息,请将选项传递-dls,告诉它不要列出目录的内容。

ls -ld t*

没有用于重命名文件的标准实用程序,因为第一个 UNIX 系统没有附带该实用程序。重命名文件的可移植方法使用循环,并且有点冗长:

for x in *.txt; do mv -- "$x" "${x%.txt}.bak"; done

有几个常用的实用程序可以重命名文件,但都不能保证在给定的 UNIX 系统上安装它们,但它们都很容易安装。以下是主要的:

  • rename来自该util-linux套件,可在每个非嵌入式 Linux 系统(而不是其他系统)上使用。在 Debian 及其衍生版本(包括 Ubuntu)上,此命令称为rename.ul.如果.txt除了最后的扩展名之外没有出现任何其他的情况,你可以写

    rename .txt .bak *.txt
    
  • rename是一个 Perl 脚本,Debian 及其衍生版本以/usr/bin/rename.您可以根据任意 Perl 命令重命名文件。

    rename 's/\.txt\z/\.bak/' *.txt
    
  • mmv,它可以根据几种基于名称的模式重命名、复制和链接文件,并且有许多与目标名称已存在时发生的情况相关的选项。请注意,必须使用引号来防止通配符被 shell 扩展。

    mmv '*.txt' '#1.txt'
    
  • zmv是一个桀骜函数,当且仅当您的 shell 是 zsh 时才可用。它可以匹配任意 zsh 模式(因此您可以根据任意正则表达式匹配文件名,而不仅仅是通配符,并且您可以根据其他条件(例如日期和大小)匹配文件)。zmv也可以复制和链接。

    zmv '(*).txt' '$1.txt'
    

如果您对所使用的机器有一定的控制权,我的建议是使用 zsh 作为您的 shell(它比 bash 有其他好处)并将这些行放入您的~/.zshrc

autoload -U zmv
alias zmv='noglob zmv -w'
alias zcp='zmv -C'
alias zln='zmv -L'
alias zsy='zmv -Ls'

noglob是 zsh 的一项功能,告诉 shell 不要在命令参数中扩展通配符。这样您就可以编写zmv *.txt \$1.txt(您始终需要保护$替换文本中的 )。

相关内容