'ls -1':如何列出不带扩展名的文件名


24

ls -1 像这样列出我的元素:

foo.png
bar.png
foobar.png
...

我希望它没有.png类似的列出:

foo
bar
foobar
...

(目录仅包含.png文件)

有人可以告诉我grep在这种情况下如何使用吗?

目的:我有一个文本文件,其中列出了所有名称而没有扩展名。我想制作一个脚本,将文本文件与文件夹进行比较,以查看缺少哪个文件。


36
您想要小心这样的请求。Linux没有文件扩展名。Linux的文件名可能包含也可能不包含.。尽管惯例说最后要用来命名文件.png,但没有理由为什么我不能将png文件命名为foo.zipor my.picture.20160518或just mypic
hymie

2
我知道@hymie,但是该文件夹中的元素都以.png结尾。
科林

14
什么是“扩展名”?这不是Unix文件命名的一部分。它来自VMS / NT / Windows。你们年轻人也从我的草坪上下来。:)
mpez0

28
我们不要高估这一点。操作系统将扩展名简单地视为文件名的一部分,但是从编译器到GUI,很多Unix程序都注意扩展名。这个概念对于unix来说绝对不是陌生的。
亚历克西斯

1
它通常建议避免解析的输出ls和管道的输出lsfind,主要是因为可能性在承担newline,`标签字符的文件名。如果文件名The new art of working on .png\NEWLINE files and other formats很多,建议的解决方案将产生问题。
哈斯图尔

Answers:


41

您只需要外壳即可完成这项工作。

POSIXly:

for f in *.png; do
    printf '%s\n' "${f%.png}"
done

zsh

print -rl -- *.png(:r)

4
有没有必要printf; echo ${f%.png}就足够了。
戴维·康拉德

11
@Conrad:在某些情况下,如果文件名以破折号开头或包含转义的序列,则使用echo无法正常工作。
cuonglm '16

3
@DavidConrad:另请参见unix.stackexchange.com/a/65819/38906
cuonglm

35
ls -1 | sed -e 's/\.png$//'

sed命令删除(即用空字符串替换).png在文件名末尾找到的所有字符串。

.作为被转义\.,使得它被解释sed为一个文字.字符,而不是正则表达式.(其装置匹配任何字符)。该$是结束线的锚,所以它不匹配.png文件名中的中间。


4
我认为OP希望删除任何扩展名,但可能只删除“最后一个”。因此,也许通过以下方式修改您本来很好的答案:sed 's/\.[^.]*$//'
Otheus

1
是的,该正则表达式在那种情况下可以工作...但是,如果OP希望这样做,他们应该这样说,而不是专门说自己“希望它在没有.png的情况下列出”
cas

4
-1是没有必要的,是这里的默认值。
jlliagre

3
@jlliagre我同意cas,-1应该指定。这是管道打开时的唯一默认设置,这对于某些人来说是一个隐藏的惊喜。因此,将其明确表示有助于理解。我也在脚本中执行此操作,因此我知道期望什么样的输出。
奥修斯

1
警告 如果文件名.png在换行符前带有键(),则您甚至会擦除该字符,.png而不仅仅是最后一个。最好避免管道分析ls的输出,它通常会保留一些隐藏的惊喜……(答案中的某些单词和参考文献更多)。
哈斯图尔

16

如果您只想使用bash:

for i in *; do echo "${i%.png}"; done

grep在尝试查找匹配项时,您应该伸出援手,而不是删除/替代,这sed更合适:

find . -maxdepth 1 -name "*.png"  | sed 's/\.png$//'

一旦确定需要创建一些子目录以使PNG文件中具有顺序,您可以轻松地将其更改为:

find . -name "*.png"  | sed 's/\.png$//'

ls -1 | sed's / .png //'效果很好。谢谢!
科林

如果找到以键()作为名称一部分并且在换行符之前的文件,则通过find 管道传递到sed解决方案可能会出现一些问题.png。最好避免管道传输或解析findor 的输出ls,它通常会保留一些隐藏的惊喜...(答案中的某些单词和参考文献更多)。
哈斯图

可能替代find的东西,如echo在最后一个例子。不清楚在此服务的目的find是什么,结果取决于目录结构(即,如果您有目录files.png

@BroSlow更新为更明智的方法。
Anthon

13

我会去basename(假设GNU实现):

basename --suffix=.png -- *.png

请注意,如果要在管道中使用它,可能会发现使用GNU basename的-z(或--zero)选项产生NUL分隔(而不是换行分隔)的输出会有所帮助。
Toby Speight

11

另一个非常相似的答案(我很惊讶这个特殊的变体还没有出现)是:

ls | sed -n 's/\.png$//p'
  • 您无需-1选择ls,因为它ls假设标准输出不是终端(在这种情况下为管道)。
  • -n选项sed的意思是“默认不打印线”
  • /p替换末尾的选项表示“ ...如果进行替换,则打印此行”。

这样做的最终效果是只打印出以结尾的行.png,并.png删除它们。也就是说,这也满足了OP问题的一般化要求,即目录不只包含.png文件。

sed -n在您可能另外使用grep + sed的情况下,该技术通常很有用。


我喜欢您以前写答案的方式。此解决方案将出现文件名(包括换行符)的问题,它将不会打印名称的第一部分。如果.png在换行符char之前加一个键()会更讨厌,那就更不用说了:在这种情况下,您将打印不带png的那部分,而不会仅删除最后一部分。通常建议避免解析(和传递)输出,ls因为问题可能隐藏在您未考虑的地方……
Hastur

2
@Hastur原则上您是正确的,并且著名的关于不解析ls的页面列出了处理病理性文件名时的其他问题(和解决方案)。但是,最好的处理方法是避免使用病理文件名(doh!);如果不能,或者如果您必须坚决反对它们,那么要么使用,要么find(可能更好)使用比sh管理它们更强大的语言(sh 可以做所有事情的事实并不意味着它是最佳选择)每个案例)。该外壳首先为可用性而设计。
诺曼·格雷

我原则上同意可用性,但是当您在文件名中包含每个换行符时,此变体将失败。例如,当您在GUI中从pdf复制并粘贴一行时,这种情况很容易被忽视,因此,您仅认为应避免使用病理文件名
哈斯图尔

而且恕我直言,它很容易开始解析ls,但是它正在解决未来的问题。当我们已经忘记了它们的局限性时,我们通常会制作一些脚本,供以后使用(这是很正常的,这是很人性的)。我提出了一个find示例(有-exec管和无管),即使我认为更好的(因为是纯壳)也可以回答cuonglm的一个,完全和posix兼容的问题。
哈斯图尔

@Hastur:无论如何,将来都会出现这些问题。系统中的许多内容对于带有换行符的文件都不可靠。例如,尝试在其上使用locatemake
reinierpost

8

您只能使用BASH命令来执行此操作(而无需任何外部工具)。

for file in *; do echo "${file%.*}"; done 

当您没有/ usr / bin时,这非常有用,它对于像this.is.image.png这样的文件名以及所有扩展名都很好用。


6

这是不安全的解析ls或管find[ 12 ]

解析(或管道传递)lsor 的输出是不安全的find,主要是因为可以在文件名中找到非常用字符作为换行符制表符 ...在这里,将使用纯shell循环[ cuonglm ]
即使使用该选项传递find命令的命令也可以使用:-exec

find ./*.png  -exec  basename {} .png  \;

更新/注释:您find .甚至可以搜索隐藏文件,或find ./*.png仅获取未隐藏的文件。在find *.png -exec ...存在一个名为的文件的情况下,您可能会遇到问题,.png因为find会将其作为选项。您可以添加-maxdepth 0以避免进入名为的目录Dir_01.png,或者find ./*.png -prune -exec ...在不允许maxdepth的情况下使用(感谢Stéphane)。如果要避免列出这些目录,则应添加选项-type f(这还将排除其他类型的非常规文件)。让我们看一下man有关所有可用选项的更完整的全景图,并记住检查它们是否符合POSIX,以获得更好的可移植性。

还有一些话

例如,可能会发生以下情况:从文档复制标题并粘贴到文件名中,一个或多个换行符将在文件名本身中结束。我们可能很不幸,标题甚至可以包含换行之前必须使用的键:

The new art of working on .png
files and other formats.

如果要测试,可以使用以下命令创建文件名

touch "A file with two lines"$'\n'"and This is the second.png"
touch "The new art of working on .png"$'\n'"files and other formats.png"

简单/bin/ls *png将输出?而不是不可打印的字符

A file with two lines?and This is the second.png
The new art of working on .png?files and other formats.png

在所有情况下,您都将通过管道传输的输出ls否则find以下命令将没有提示来了解当前行是否来自新文件名,或者该行是否位于先前文件名中换行符之后。一个讨厌的名字确实,但还是一个法律问题。

${parameter%word}在带有printf或的变体中,带有shell Parameter-Expansion的shell循环echo将工作[ cuonglm ],[ Anthon1 ]

for f in *.png; do printf "%s\n" "${f%.png}" ; done

在Shell参数扩展的手册页中[ 3 ]

$ {parameter%word}
$ {parameter %% word}

...扩展的结果是删除了最短匹配模式(%情况)或最长匹配模式(%%情况)的参数值。


find命令的结果也是位变量(例如,如果有一个名为的目录files.png

1
亲爱的@BroSlow,当我在上面写下答案时, 我尝试了通过命令行在脚本中作为外壳调用的参数启动的那一刻出现的其他13个(所有)变体。请执行相同操作,并告诉我它们是否按照您的预期方式运行。我使用bash 4.3.11dash 0.5.7-4,zsh(必要时)5.0.2。进行了测试。请随意阅读这篇添加更多内容的文章。我同意管道输出的注释find,为此我明确建议-exec,并在标题中写道。:-)
哈斯图尔

再次重新阅读Wiki。我仍然认为您需要在示例中使用管道,因为此处正在讨论。对于大多数现代版本,ls当通过管道传输或重定向输出时,都没有问题,但是正如Wiki中所提到的,它可能不适用于所有人。?当输出发送到终端时,大多数只会在特殊字符的位置插入。即做echo *.png | od -cls *.png | od -c。换行问题不是问题ls,这是任何不为空的两端在管道两端终止的命令的问题。

1
printf "${f%.png}\n"是错的。第一个参数是格式,您不应在其中使用变量数据。甚至可以看作是DoS漏洞(例如尝试使用%1000000000s.png文件)。
斯特凡Chazelas

你需要find ./*.png -prune -exec...或者你有开头的文件名的问题-(和类型目录的文件,注意,-maxdepth不可移植)
斯特凡Chazelas

4

还不够吗?

ls -1 | sed 's/\.png//g'

或一般来说

ls -1 | sed 's/\.[a-z]*//g'

将删除所有扩展


是的,但是其他解决方案也可以。
科林

我的意思是说,您的问题始于ls -1,所以ls -1应该这样做。:)
罗哈尔·阿巴斯

-1是没有必要的,是这里的默认值。
jlliagre

@Rohail Abbas但是不是每个系统都安装了sed吗?
科林

1
的确如此,但是ls当它的输出不是终端时,如果没有该选项,它还是会这样做,这就是这种情况。
jlliagre

3

用途rev

ls -1 | rev | cut -f 2- -d "." | rev

rev反转所有字符串(行);您在第一个'之后切掉了所有内容。和rev撤消剩余。

如果您想grep“母校”:

ls -1 | rev | cut -f 2- -d "." | rev | grep 'alma'

-1是没有必要的,是这里的默认值。
jlliagre

2
这在名为My.2016.Summer.Vacation.png
David Conrad

@DavidConrad我的坏事:/我已更正为cut -f 2-
汤姆·

现在它适用于该文件,但不适用于带有.png和换行符的文件...建议避免解析,ls因为它喜欢很好地隐藏惊喜... :-)
Hastur

2

如果我知道该目录仅包含扩展名为.png的文件,则可以运行: ls | awk -F. '{print $1}'

这将为存在filename.extension的任何内容返回第一个“字段”。

例:

[rsingh@rule51 TESTDIR]$ ls
10.png  1.png  2.png  3.png  4.png  5.png  6.png  7.png  8.png  9.png

[rsingh@rule51 TESTDIR]$ ls | awk -F. '{print $1}'
10
1
2
3
4
5
6
7
8
9

不幸的是,如果文件名超过一个.,所有文件名都将失败Image.1.png,甚至是名字不佳且内部带有特殊字符的文件名也会失败。作为新行或一个你会在(输入)记录分隔符使用awkRS。建议避免解析ls输出,因为它喜欢隐藏当您不期望出现的问题。您可以在参考文献12中阅读更多内容。顺便说一句,使用awk的想法很好...我在一个答案中列举了一些示例。
哈斯图尔

的确如此,但是考虑到Colin提供的示例,它会很好地工作。为了使其适合您的建议,我可能将其更改为:[rsingh @ rule51 TESTDIR] $ ls | sed -e's / .png $ //'10 1 2 3 4 5 6 7 8 9 harry.the.bunny whats.a.png.filename并不是很难,但是鉴于Colin的需要,我不确定问题将解析ls。
rsingh '16

抱歉...我只是意识到在sed修改'ls'的输出之前,我没有显示文件的目录[rsingh @ rule51 TESTDIR] $ ls 10.png 2.png 4.png 6.png 8.png 8.png harry.the.bunny.png 1.png 3.png 5.png 7.png 9.png whats.a.png.filename.png [rsingh @ rule51 TESTDIR] $ ls | sed -e's / .png $ //'10 1 2 3 4 5 6 7 8 9 harry.the.bunny whats.a.png.filename
rsingh '16

注1,你需要躲避.\.里面的sed -e 's/\.png$//',但可以使其变成一个答案刚写。:-( note2您可以尝试使用awk类似ls | awk -F. '{if ($NF=="png") {for (i=1;i<NF-1;i++) printf("%s.", $i) ; printf $(NF-1)"\n"}}'...的方法,但是您将始终遇到awk无法知道行名是否在文件名后的换行符的问题。我试图在回答中说得更好。
哈斯塔

感谢Hastur,我错过了:)。另外,在这种情况下,我放弃使用awk来支持sed。
rsingh '16

2

根据您的评论“我有一个文本文件,其中列出了所有名称而没有扩展名。我想制作一个PHP脚本,将文本文件与文件夹进行比较,以查看缺少哪个文件”:

for file in $(cat yourlist) ; do
  [ -f "${file}.png" ] || {
    echo "$file : listed in yourlist, but missing in the directory"
  }
done
#assumes that filenames have no space...
# otherwise use instead:
#  while IFS= read file ; do ...(same inner loop as above)... ; done < yourlist

相反:

for file in *.png ; do
  grep "^${file%.png}$" yourlist >/dev/null || {
    echo "$file: present in the directory but not listed in yourlist"
  }
done
#I assume there are no spaces/tabs/? before/after names in 'yourlist'. Change the script accordingly if there are some (or sanitize the list)

1

ls -l | sed 's/\.png$//'

是@roaima强调的最准确的方法。如果没有逃脱\.png命名的文件a_png.png将被列为:a_


使用时ls -l,使用会给出文件的详细信息,这不是OP所要求的。
Anthon

1

简单的外壳线(ksh,bash或zsh;不是破折号):

set -- *.png; printf '%s\n' "${@%.png}"

一个简单的功能(来自No Extension):

ne(){ set -- *.png; printf '%s\n' "${@%.png}"; }

或删除给定扩展名的函数(默认为png):

ne(){ ext=${1:-png}; set -- *."$ext"; printf '%s\n' "${@%.${ext}}"; }

用于:

ne jpg

如果输出为星号*,则不存在具有该扩展名的文件。


1

您可以尝试以下供稿,aws的超级输出是“。”。由于所有文件都将具有name.png,因此您将打印第一列:
ls | awk -F"." '{print $1}'


-1

如果您有sed的权限,这样做会更好,因为无论它是什么(png,jpg,tiff等),它都会删除最后一个文件扩展名。

ls | sed -e 's/\..*$//'

7
中断文件名,如this.is.a.dotty.txt。试试吧s/\.[^.]*$//
roaima
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.