【问题标题】:Bash: cant' read out strings with spaces after looping through array of stringsBash:在遍历字符串数组后无法读出带空格的字符串
【发布时间】:2011-09-01 07:10:57
【问题描述】:

我正在使用循环来读取数组的内容,该数组包含名为“music”的目录层次结构中的所有目录和文件(内容是“find”命令的先前输出的字符串)。这个想法是根据流派、艺术家和标题将“directory_contents”中每个数组元素的完整目录路径分成子字符串。由于我的音乐目录首先按流派排序,然后按艺术家排序,然后按标题排序,因此我使用 awk 抓取每个相关项目,其中分隔符“/”出现。例如,如果使用 find "./Electronic/Squarepusher/My Red Hot Car.aif" 后目录看起来像这样,我将把 "Electronic"、"Squarepusher" 和 "My Red Hot Car" 分开,然后分别存储在流派、艺术家和标题的单独数组中。稍后我将对这些数据进行排序,然后将排序后的输出通过管道传输到另一个实用程序中,以在一个漂亮的表中打印所有目录内容(还没有这样做)。目前,我只是尝试使用 echo 语句查看字符串分离的结果,并且在大多数情况下它似乎有效。但是,我似乎无法提取包含空格的子字符串,这不好:

-->./Hip-Hop/OutKast/title1.aif<--
Genre:
Hip-Hop
Artist:
OutKast
Title:
title1

-->./Hip-Hop/OutKast/title2.aif<--
Genre:
Hip-Hop
Artist:
OutKast
Title:
title2

-->./Hip-Hop/OutKast/title3.aif<--
Genre:
Hip-Hop
Artist:
OutKast
Title:
title3

-->./Jazz/John<--
Genre:
Jazz
Artist:
John
Title:


-->Coltrane/title1.aif<--
Genre:
title1.aif
Artist:

Title:

如您所见,当循环读取字符串“John Coltrane”时,它将空格视为分隔符,并将“John”之后的所有内容视为不同的文件名。我尝试在“数组”部分以及此处的其他帖子下的 bash 手册中寻找解决方案,但找不到适用于我的特定问题的解决方案(抱歉)。如果有人有想法,他们将不胜感激。有问题的代码出现在下面的 for 循环中(我没有发布整个脚本,因为它很长,但如果需要,请告诉我):

#more before this...

#declare variables                                                                                                      
declare -a genre_list                                                                                                   
declare -a title_list                                                                                                   
declare -a artist_list                                                                                                  
declare -a directory_contents                                                                                           


#populate directory with contents                                                                                       
cd $directory                                                                                                           
directory_contents=$(find .  -mindepth 1 -type f)                                                                       
cd ..                                                                                                                   


for music_file in ${directory_contents[*]}; do                                                                          
    if [[ $DEBUG = "true" ]] ; then                                                                                     
        echo "-->$music_file<--"                                                                                        
    fi                                                                                                                  

    echo "Genre:"                                                                                                       
    echo $music_file | awk -F"/" '{print $2}'                                                              
    echo "Artist:"                                                                                                      
    echo $music_file | awk -F"/" '{print $3}'                                                               
    echo "Title:"                                                                                                       
    echo $music_file | awk -F"/" '{print $4}' | awk -F"." '{print $1}'                                     
    echo ""                                                                                                             
done   

【问题讨论】:

    标签: arrays bash string for-loop awk


    【解决方案1】:

    你为什么不简单地在一行中做:

    cd $directory && \
    find .  -mindepth 3 -maxdepth 3 -type f | \ 
    awk -F'/' '{split($4,A,".aif"); printf "Genre: %s\nArtist: %s\nTitle: %s\n\n",$2,$3,A[1];}'
    

    更新: (从标题部分删除了 .aif

    【讨论】:

    • 这个比我的解决方案优雅得多。但是,我需要通过过滤掉“。”之后的所有内容来进一步修改最后一个参数 $4。在字符串中单独获取标题(不是“title.aif”)。不确定这是否适用于这种配置?不过谢谢!
    • @Dylan,“.aif”现在也被删除了。 :)
    • 文件名也可以包含换行符,因此您可能还应该使用 -print0 和 RS=$'\0'。但是,只有 gawk 支持这一点。其他 awk 实现不接受空字符作为记录分隔符 (AFAIK)。顺便说一句,为什么-mindepth 1?为什么不 -mindepth 3 -maxdepth 3?
    • @Pianosaurus,Dylan 遇到了空格问题,他的文件名中似乎没有换行符,所以不必担心。感谢您指出深度问题。 (我会相应地更新。)
    • @ssapkota,我总是担心这些事情。可能是因为我很少编写仅我使用的脚本。 =)
    【解决方案2】:

    如果可以的话,你应该使用 Perl 来完成这个任务:

    #! /usr/bin/perl
    
    foreach my $fname (<*/*/*>) {
      next unless -f $fname;
      next unless $fname =~ m"^([^/]+)/([^/]+)/([^/.]+)\.\w+$";
      my ($genre, $artist, $title) = ($1, $2, $3);
      printf("Genre: %s\nArtist: %s\nTitle: %s\n\n", $genre, $artist, $title);
    }
    

    它比 shell 更快、更简单,并且不受文件名中的空格的影响。

    【讨论】:

    • 感谢您的建议,虽然我之前没有做过任何 Perl 编程,而且这项作业的要求是使用 Bash。
    【解决方案3】:
    MUSICDIR="/some/path"
    cd "$MUSICDIR"
    
    find  . -type f -print | (IFS=/;while read dot genre artist title
    do
        echo =$genre= =$artist= =$title=
    done)
    

    【讨论】:

    • while循环中的“点”参数是什么意思?
    • 如果您手动运行 find 命令,您会看到所有文件名都以“./”开头。 “点”变量将只包含“.”。
    【解决方案4】:

    您可以尝试设置 bash 使用的分隔符。也许换行符?

    IFS=\n
    

    【讨论】:

    • 谢谢,这个做了一些修改;我需要改用 IFS=$'\n' 。完美运行!
    猜你喜欢
    • 1970-01-01
    • 2012-08-31
    • 2022-01-16
    • 2021-11-27
    • 2014-06-16
    相关资源
    最近更新 更多