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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21245476/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 09:18:36  来源:igfitidea点击:

parse result of `ls -l` with bash script

linuxbashshellawk

提问by Matteo

I need to store the name of every file contained in a directory with a bash script and processes it in some way:

我需要使用 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

with the following command I'm able to split the result of ls -lin between all the spaces and then access the last element, which contains the name:

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

ls -l | awk '{split(
-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
,array," ")} END{print array[9]}'

However it returns only the last line (i.e. Videos) so I need to iterate it over all the lines returned by the ls -lcommand.

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

  • how can I do this?
  • Is there a better way to approach this whole problem?
  • 我怎样才能做到这一点?
  • 有没有更好的方法来解决整个问题?

ADDED PART

增加部分

To be a little more specific on what I need to do:

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

For all the files contained in a directory if it is a file I won't do anything, if it is a directory I should append the name of the directory to all the files it contains.

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

So supposing the directory Videos has the files:

所以假设目录 Videos 有文件:

-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

I need to rename them as follows:

我需要按如下方式重命名它们:

echo *

回答by Reinstate Monica Please

A better way would be to use bash globbing

更好的方法是使用 bash globbing

Just listing all files

只列出所有文件

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

Or doing something with them

或者和他们一起做点什么

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

Or recursively with bash 4+

或者递归地使用 bash 4+

#!/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

Update to get directory name and append it to all files within it

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

Replace mvwith an echoto test what it does. Also note ${file##*.}assumes the extension is everything after the last period, so if you had a file like file.tar.gzin directory on, below would turn it into file.tar_on.gz. As far as I know there is no easy way to handle this problem, though you could skip files with multiple .if you want)

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

> 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

e.g.

例如

 extension=${file##*.} 

Explanation

解释

In bash you can do this

在 bash 你可以这样做

  • ${parameter#word}Removes shortest matching prefix
  • ${parameter##word}Removes longest matching prefix
  • ${parameter%word}Removes shortest matching suffix
  • ${parameter%%word}Removes longest matching suffix
  • ${parameter#word}删除最短匹配前缀
  • ${parameter##word}删除最长匹配前缀
  • ${parameter%word}删除最短的匹配后缀
  • ${parameter%%word}删除最长的匹配后缀

To remove everything anything (*) before and including the last period, I did below

要删除所有内容 ( *) 之前并包括上一期,我在下面做了

filename=${file%.*}

To remove everything including and from the last period, I did below (think about shortest match here as going from right to left, e.g. *looks for any non-period text right to left, then when it finds a period it removes that whole section)

为了删除包括和从上一期开始的所有内容,我在下面做了(将这里的最短匹配视为从右到左,例如从右到左*查找任何非周期文本,然后当它找到一个周期时它会删除整个部分)

filename=${filename##*/}

To remove everything up to and including the last /, I did below.

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

ls > myfile.txt

Some other notes:

其他一些注意事项:

  • "$d/${filename}_${name}.$extension"Variables can have _so I switched syntax for a couple of variables here for it to work
  • "$d"/*Expands to every file of any type (regular, dir, symlink etc...) directly in "$d"
  • "$d/${filename}_${name}.$extension"变量可以有,_所以我在这里切换了几个变量的语法以使其工作
  • "$d"/*直接在“$d”中扩展到任何类型(常规、目录、符号链接等)的每个文件

回答by Floris

What is wrong with

出什么问题了

ls -l | awk '{print }'

This will only list the file names (nothing else) and send them to myfile.txt

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

If you want to go the awkroute, just do

如果你想走这awk条路,就去做

ls -l | awk '{a[NR]=}'

The default action of awkis to split fields on space - and this prints the 9th field for every line…

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

If you want to do other things with the file names, you can just extend your awk script. For example, an array with these file names could be created with

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

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

and you can use this array (called a) in further processing. If the processing requires something other than awk(from the comments I think it does), you would be better off with something that looks like

您可以a在进一步处理中使用此数组(称为)。如果处理需要的东西不是awk(从我认为的评论中),你会更好地看起来像

./listdir .

Save this as listdirin your current directory, and you're good to go.

将其另存为listdir当前目录中,您就可以开始了。

touch /var/myapp/*

Will list the entire directory, recursing down (with full relative path appended) as needed.

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

If you want this to be available "from anywhere" (it is a pretty useful command after all) you would put it somewhere in your path (and do a "rehash" command so it will be "known"); then you don't need the ./at the start of the command.

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

回答by John Kugelman

Good question! Glad you asked. Parsing ls's output is rarely the right thing to do. There are myriad ways to process a list of files. It depends what you want to do with them.

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

Here are some examples of things you can do. I've used touchas an example command. Replace that with whatever command or commands you want to do.

以下是一些您可以执行的操作的示例。我已用作touch示例命令。将其替换为您想要执行的任何命令。

  1. To run a command over multiple files, often you can simply pass all the files on the command-line.

    for file in *; do
        touch "$file"
    done
    
  2. To loop over the files in the current directory:

    for file in /some/dir/*; do
        touch "$file"
    done
    
  3. To loop over files in another directory:

    find . -name '*.txt' -exec mv {} {}.bak \;
    
  4. To rename files named *.txtto '*.bak', both here and in sub-directories:

    find ~bob/ -name '*.jpg' -delete
    
  5. To delete JPEGs in Bob's home directory (damn you Bob and your wandering eyes):

    find /dir/to/search -print0 | while read -d $'
    touch /var/myapp/*
    
    ' file; do echo "$file" touch "$file" if [[ -L $file ]]; then # $file is a symlink, do something special fi done
  6. To loop over files recursively and do complicated things to them:

    for file in *; do
        touch "$file"
    done
    
  1. 要对多个文件运行命令,通常只需在命令行上传递所有文件即可。

    for file in /some/dir/*; do
        touch "$file"
    done
    
  2. 遍历当前目录中的文件:

    find . -name '*.txt' -exec mv {} {}.bak \;
    
  3. 要循环另一个目录中的文件:

    find ~bob/ -name '*.jpg' -delete
    
  4. 要将*.txt此处和子目录中命名为“*.bak”的文件重命名:

    find /dir/to/search -print0 | while read -d $'##代码##' file; do
        echo "$file"
        touch "$file"
    
        if [[ -L $file ]]; then
            # $file is a symlink, do something special
        fi
    done
    
  5. 要删除 Bob 主目录中的 JPEG(该死的 Bob 和你流浪的眼睛):

    ##代码##
  6. 递归地遍历文件并对它们做复杂的事情:

    ##代码##

回答by ctrl-alt-delor

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

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

or

或者

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

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

but why not just ls?

但为什么不只是ls