使用bash脚本解析`ls -l`的结果

时间:2014-01-20 22:28:29

标签: linux bash shell awk

我需要使用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," ")} END{print array[9]}'

但是它只返回最后一行(即Videos)所以我需要在ls -l命令返回的所有行上迭代它。

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

添加部分

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

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

假设视频目录中包含以下文件:

-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

4 个答案:

答案 0 :(得分:5)

更好的方法是使用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 

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

mv替换为echo以测试它的作用。另请注意,${file##*.}假定扩展名是上一个句点之后的所有内容,因此如果您在目录file.tar.gz中有on这样的文件,则下面会将其转换为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

e.g。

> 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”中扩展到任何类型的每个文件(常规,dir,符号链接等...)

答案 1 :(得分:3)

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

以下是您可以执行的一些示例。我使用touch作为示例命令。用你想做的任何命令或命令替换它。

  1. 要在多个文件上运行命令,通常只需在命令行上传递所有文件。

    touch /var/myapp/*
    
  2. 循环遍历当前目录中的文件:

    for file in *; do
        touch "$file"
    done
    
  3. 循环遍历另一个目录中的文件:

    for file in /some/dir/*; do
        touch "$file"
    done
    
  4. 要在此处和子目录中将名为*.txt的文件重命名为'* .bak':

    find . -name '*.txt' -exec mv {} {}.bak \;
    
  5. 删除Bob主页目录中的JPEG(该死的Bob和你流浪的眼睛):

    find ~bob/ -name '*.jpg' -delete
    
  6. 以递归方式循环文件并对它们执行复杂的操作:

    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
    

答案 2 :(得分:1)

有什么问题
ls > myfile.txt

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

如果您想要awk路线,请执行

ls -l | awk '{print $9}'

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

如果你想用文件名做其他事情,你可以扩展你的awk脚本。例如,可以使用

创建具有这些文件名的数组
ls -l | awk '{a[NR]=$9}'

您可以在进一步处理中使用此数组(称为a)。如果处理需要除awk以外的其他内容(根据我认为的评论),那么您最好使用类似的内容

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

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

./listdir .

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

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

答案 3 :(得分:1)

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

ls -l | awk '{print $9}'

但为什么不只是ls