用bash脚本解析`ls -l`的结果[重复]

Posted

技术标签:

【中文标题】用bash脚本解析`ls -l`的结果[重复]【英文标题】:parse result of `ls -l` with bash script [duplicate] 【发布时间】:2014-02-10 07:12:15 【问题描述】:

我需要使用 bash 脚本存储目录中包含的每个文件的名称,并以某种方式对其进行处理:

drwxrwxr-x  5 matteorr matteorr  4096 Jan 10 17:37 Cluster
drwxr-xr-x  2 matteorr matteorr  4096 Jan 19 10:43 Desktop
drwxrwxr-x  9 matteorr matteorr  4096 Jan 20 10:01 Developer
drwxr-xr-x 11 matteorr matteorr  4096 Dec 20 13:55 Documents
drwxr-xr-x  2 matteorr matteorr 12288 Jan 20 13:44 Downloads
drwx------ 11 matteorr matteorr  4096 Jan 20 14:01 Dropbox
drwxr-xr-x  2 matteorr matteorr  4096 Oct 18 18:43 Music
drwxr-xr-x  2 matteorr matteorr  4096 Jan 19 22:12 Pictures
drwxr-xr-x  2 matteorr matteorr  4096 Oct 18 18:43 Public
drwxr-xr-x  2 matteorr matteorr  4096 Oct 18 18:43 Templates
drwxr-xr-x  2 matteorr matteorr  4096 Oct 18 18:43 Videos

使用以下命令,我可以在所有空格之间拆分 ls -l 的结果,然后访问包含名称的最后一个元素:

ls -l | awk 'split($0,array," ") ENDprint array[9]'

但是它只返回最后一行(即Videos),所以我需要遍历ls -l 命令返回的所有行。

我该怎么做? 有没有更好的方法来解决整个问题?

添加部分

更具体地说明我需要做什么:

对于目录中包含的所有文件,如果它是一个文件,我不会做任何事情,如果它是一个目录,我应该将目录的名称附加到它包含的所有文件。

所以假设目录 Videos 有文件:

-rwxr-xr-x  2 matteorr matteorr  4096 Oct 18 18:43 video1.mpeg
-rwxr-xr-x  2 matteorr matteorr  4096 Oct 18 18:43 Video2.wmv

我需要将它们重命名如下:

-rwxr-xr-x  2 matteorr matteorr  4096 Oct 18 18:43 video1_Videos.mpeg
-rwxr-xr-x  2 matteorr matteorr  4096 Oct 18 18:43 Video2_Videos.wmv

【问题讨论】:

如果您关心脚本的稳健性(对奇怪文件名的稳健性),更好的方法可能是使用 find 而不是 ls:mywiki.wooledge.org/ParsingLs 你想做什么? @richard - 我需要存储目录的名称,然后将其附加到它包含的所有文件中 你打算如何处理每个文件? 你能举个例子吗? 【参考方案1】:

更好的方法是使用 bash globbing

只列出所有文件

echo *

或者和他们一起做点什么

for file in *; do
  echo "$file" # or do something else
done

或使用 bash 4+ 递归

shopt -s globstar
for file in **/*; do
  echo "$file" # or do something else
done 

更新以获取目录名称并将其附加到其中的所有文件

echo 替换mv 来测试它的作用。另请注意$file##*. 假定扩展名是最后一个句点之后的所有内容,因此如果您在目录on 中有类似file.tar.gz 的文件,下面会将其转换为file.tar_on.gz。据我所知,没有简单的方法来处理这个问题,但如果你愿意,你可以跳过包含多个 . 的文件)

#!/bin/bash
d="/some/dir/to/do/this/on"
name=$d##*/ #name=on
for file in "$d"/*; do
  extension=$file##*. 
  filename=$file%.*
  filename=$filename##*/
  [[ -f $file ]] && mv "$file" "$d/$filename_$name.$extension"
done

例如

> ls /some/dir/to/do/this/on
video1.mpeg  Video2.wmv
> ./abovescript
> ls /some/dir/to/do/this/on
video1_on.mpeg  Video2_on.wmv

说明

在 bash 中你可以这样做

$parameter#word 删除最短匹配前缀 $parameter##word 删除最长匹配前缀 $parameter%word 删除最短匹配后缀 $parameter%%word 删除最长匹配后缀

要删除所有内容(*),包括最后一个时期,我在下面做了

 extension=$file##*. 

要删除包括最后一个句点在内的所有内容,我在下面做了(考虑从右到左的最短匹配,例如* 从右到左查找任何非句点文本,然后当它找到句点时它会删除整个部分)

filename=$file%.*

要删除直到并包括最后一个 / 的所有内容,我在下面做了。

filename=$filename##*/

其他一些注意事项:

"$d/$filename_$name.$extension" 变量可以有 _ 所以我在这里切换了几个变量的语法以使其工作 "$d"/* 直接在 "$d" 中扩展到任何类型(常规、目录、符号链接等)的每个文件

【讨论】:

感谢您的精彩回答!我现在正在尝试 @Matteo 没问题。如果您有一个带有多点扩展名的文件,请参阅我在脚本上方的评论。处理这个问题会变得更加混乱,并且可能涉及一个具有已知多点扩展名的数组并对其进行测试或类似的东西。 脚本运行良好,能否请您多评论一下?我对所有#,%,.,/,* 字符感到困惑...... @Matteo 更新了解释。另外,我忘记了原始脚本中相当重要的部分。如果您只想 mv 常规文件,则应该执行 [[ -f $file ]] && mv 之类的操作,因为 * 会扩展到所有文件,包括子目录。 +1 以获得一些非常酷的 bash 技巧。我每天都在这里学到新东西。今天,这个答案是“新事物”。谢谢!注意 - 我修正了一些错别字。请确保我没有意外更改您答案的含义。【参考方案2】:

有什么问题

ls > myfile.txt

这只会列出文件名(没有别的)并将它们发送到myfile.txt

如果你想走awk 路线,就这样做

ls -l | awk 'print $9'

awk 的默认操作是在空间上拆分字段 - 这会打印每行的第 9 个字段...

如果你想对文件名做其他事情,你可以扩展你的 awk 脚本。例如,可以使用以下命令创建具有这些文件名的数组

ls -l | awk 'a[NR]=$9'

您可以在进一步处理中使用这个数组(称为a)。如果处理需要 awk 以外的其他东西(我认为它来自 cmets),那么看起来像这样的东西会更好

#!/bin/bash
for f in $1"/"*
do
if [ -d "$f" ] ; then
  ./listdir $f
else
  echo $f
fi
done

在您的当前目录中将其保存为listdir,然后您就可以开始了。

./listdir .

将列出整个目录,并根据需要向下递归(附加完整的相对路径)。

如果您希望它“从任何地方”可用(毕竟这是一个非常有用的命令),您可以将它放在您的路径中的某个位置(并执行“rehash”命令,使其成为“已知”);那么你就不需要在命令开头的./了。

【讨论】:

myfile.txt 来自哪里?这不是问题。 thks,但是我怎样才能遍历列表中的一项呢? @richard - 问题是“我需要存储”……我决定将它存储在一个文件中。 @matteo - 你想如何迭代?我给的 awk 行为每次迭代提供了一个值。您可以将其他表达式放入 awk 脚本中。 对不起,你是对的。【参考方案3】:

好问题!很高兴你问。解析ls 的输出很少是正确的做法。有无数种方法可以处理文件列表。这取决于你想用它们做什么。

这里有一些你可以做的事情的例子。我使用touch 作为示例命令。将其替换为您想要执行的任何命令。

    要对多个文件运行命令,通常您只需在命令行中传递所有文件。

    touch /var/myapp/*
    

    遍历当前目录中的文件:

    for file in *; do
        touch "$file"
    done
    

    遍历另一个目录中的文件:

    for file in /some/dir/*; do
        touch "$file"
    done
    

    在此处和子目录中将名为 *.txt 的文件重命名为“*.bak”:

    find . -name '*.txt' -exec mv  .bak \;
    

    删除 Bob 主目录中的 JPEG(该死的 Bob 和你的眼睛):

    find ~bob/ -name '*.jpg' -delete
    

    递归循环文件并对它们做复杂的事情:

    find /dir/to/search -print0 | while read -d $'\0' file; do
        echo "$file"
        touch "$file"
    
        if [[ -L $file ]]; then
            # $file is a symlink, do something special
        fi
    done
    

【讨论】:

请注意find -print0 是一个不可移植的GNU find 扩展。【参考方案4】:

ls -l | awk 'split($0,array," ") print array[9]'

ls -l | awk 'print $9'

但为什么不只是ls

【讨论】:

thks,但是我怎样才能遍历 item 上的列表呢? ls 如果重定向到终端以外的其他地方,则将输出放在一列中。输入ls | cat 进行检查。如果你真的不相信自动终端检测会起作用,你可以使用ls -1(这是一个数字,不是小写字母L)

以上是关于用bash脚本解析`ls -l`的结果[重复]的主要内容,如果未能解决你的问题,请参考以下文章

如何将bash脚本中的json解析为数组?数组值应该同时具有 key:value 格式

通过bash启动python脚本时权限被拒绝

从 bash 脚本中通过 ssh 在远程主机上执行命令

Linux批量解压缩脚本

vi,ls这些命令都不能用了,怎么办

Linux试题