(这是一个在更混乱的环境中最简单的工作示例。)
我有一个d
包含文件的目录1.txt 2.txt 3.txt 4.wav
。我当前的目录在其他地方。
ls d
报告1.txt 2.txt 3.txt 4.wav
,理应如此。
但我想要的只是1.txt 2.txt 3.txt
,所以我努力ls d/*.txt
。
但这会d/
在每个文件名前面加上 a: d/1.txt d/2.txt d/3.txt
,因为 shell在看到它*
之前扩展了ls
它。
为了获得我想要的1.txt 2.txt 3.txt
,我可以ls d | grep txt
,或者ls d/*.txt | xargs basename -a
,甚至产生一个新的外壳:(cd d; ls *.txt)
。有没有更好的方法,不那么笨重,更不容易受到陷阱和极端情况的影响,来过滤 的输出ls
?
答案1
和zsh
:
printf '%s\n' d/*.txt(:t)
:t
与 csh 历史修饰符类似,但在 glob 限定符中,可以让您获得尾巴文件名的。
还:
files=(d/*.txt)
printf '%s\n' $files:t
在其他类似 Bourne 的 shell 中,您始终可以执行以下操作:
(cd d && printf '%s\n' *.txt)
请注意,它不会分叉新的 shell,而是创建一个子 shell 环境。在大多数 shell 实现中,子 shell 环境是通过分叉子进程来实现的,但新的 shell 不会在其中执行(它不是新 shell,而是在新进程中分叉的同一个 shell)。另请注意,如果子 shell 中的最后一个命令是外部命令,则大多数 shell(尽管不是bash
)不会派生额外的进程,因此运行的进程总数将与没有子 shell 环境时相同。
ksh93
不会 fork 子 shell。它通过在退出子 shell 时撤消在子 shell 中完成的修改来实现此目的。因此,那里(哪里printf
也是内置的)(cd d && printf '%s\n' *.txt)
不会分叉额外的进程。
另请注意,ls
列出了文件和内容它作为参数传递的目录。在这里,ls
如果只是为了打印 shell 给出的名称,则不需要,但如果您坚持使用ls
,则应该传递该-d
选项,以便它不会列出目录的内容:
(cd d && ls -d -- *.txt)
答案2
我相信按照您的要求做的最简单的方法是:
$ ( cd d; ls *.txt )
1.txt 2.txt 3.txt
这是在子 shell 内发生的,( ... )
因此目录更改不是永久性的,仅对执行这两个命令有效。
更强大的版本是:
$ ( cd d && ls -d -- *.txt )
1.txt 2.txt 3.txt
除非ls
目录更改成功,否则不会执行其中的内容,并ls
列出目录而不是其内容,并且不将名称以破折号开头的文件作为选项。
如果您不介意更改位置参数($1
、$2
等):
这也相对简单:
$ set d/*.txt
$ for f do printf '%s\n' "${f#d/}"; done
1.txt
2.txt
3.txt
这将在 POSIX shell 中工作:
$ dash -c 'set d/*.txt; for f do printf "%s\n" "${f#d/}"; done'
1.txt
2.txt
3.txt
你可以使用 GNU find 的-printf
:
$ find d -maxdepth 1 -iname "*.txt" -printf "%f\n" | sort
1.txt
2.txt
3.txt
但这已经变得足够复杂了。抱歉,据我所知,没有更多“简单”的解决方案。