이 명령을 어떻게 반복할 수 있나요?

이 명령을 어떻게 반복할 수 있나요?

여기에서 Ubuntu Linux를 실행합니다.

PWD에서 모든 mp3 파일을 찾고, mp3info를 사용하여 각각의 지속 시간을 분 단위로 얻고, 합산하고, pwd에 있는 모든 mp3의 총 지속 시간을 인쇄하는 터미널 명령이 있습니다.

for file in *.mp3; do 
  mp3info -p "%S\n" "$file"
done | paste -sd+ | sed 's+\(.*\)+(\1)/60+' | bc

출력 예:

$ for file in *.mp3; do 
  mp3info -p "%S\n" "$file"
done | paste -sd+ | sed 's+\(.*\)+(\1)/60+' | bc
47

따라서 PWD에는 47분 분량의 mp3가 있습니다.

나는 이것을 모든 하위 디렉토리로 재귀하고, 이름을 인쇄하고, 각 폴더에 있는 모든 mp3의 총 지속 시간을 나열하는 bash 스크립트로 만들고 싶습니다. 예를 들면 다음과 같습니다.

foldernameA 
45 
foldernameB 
89 
foldernameC 
17

등.

내가 시도한 것("durations.sh"):

#!/bin/bash
find . -type d -execdir sh -c 'for file in *.mp3; 
do 
  mp3info -p "%S\n" "$file"; 
done 
| paste -sd+ | sed 's+\(.*\)+(\1)/60+' | bc

그러나 이것은 비참하게 실패합니다.

$ ./durations.sh
./durations.sh: line 6: syntax error near unexpected token `('
./durations.sh: line 6: `| paste -sd+ | sed 's+\(.*\)+(\1)/60+' | bc'

나는 분명히 내가 무엇을 하고 있는지 모른다.

답변1

for 루프를 직접 사용할 수 있습니다shopt -s 글로스타:

글로벌 스타

설정된 경우 파일 이름 확장자 컨텍스트에 사용된 "**" 패턴은 모든 파일과 0개 이상의 디렉터리 및 하위 디렉터리와 일치합니다. 패턴 뒤에 "/"가 오면 디렉터리와 하위 디렉터리만 일치합니다.

shopt -s globstar

d=0;
for file in **/*.mp3; do
  d=$((d + $(mp3info -p "%S" "$file")))
done
mins=$(echo "$d / 60" | bc)
secs=$(echo "$d % 60" | bc)

echo "Total $mins minutes and $secs seconds"

답변2

단일 폴더의 길이를 나열하려면 이중 루프가 필요합니다. 첫 번째 루프는 디렉터리를 나열하고, 두 번째 루프는 각 디렉터리의 파일을 나열합니다.

#!/bin/bash
OIFS="$IFS"
IFS=$'\n'

function secondToTime () { #Convert second to Day, Hours, Minutes, Seconds
    seconds=$1
    min=0
    hour=0
    day=0
    if((seconds>59));then
        ((sec=seconds%60))
        ((seconds=seconds/60))
        if((seconds>59));then
            ((min=seconds%60))
            ((seconds=seconds/60))
            if((seconds>23));then
                ((hour=seconds%24))
                ((day=seconds/24))
            else
                ((hour=seconds))
            fi
        else
            ((min=seconds))
        fi
    else
        ((sec=seconds))
    fi
    echo "$day"d "$hour"h "$min"m "$sec"s
  }

case $1 in #loop though the first argument
  '-h'|'--help')     # Display the help and exit
    echo "Usage: $0 [PATH]"
    echo "Display the total play time of each folder"
    exit 0
    ;;

  !'')      # Will use the argument as target path
    target=$1
    ;;

  *)        # If no argument is specify it will use the current path
    target='.'
    ;;
esac



for folders in `find $1 -type d ` # Find all sub folders in the specifyed path
do
    for folder in $folders # Loop though each folders
    do
      echo Folder $folder:
    folderTime=0;
        for file in `ls $folder/*.mp3 2> /dev/null` #loop though each files in each folders
        do
            fileTime=`mp3info -p "%S\n" "$file"` #get the time lenght of $file
            isNumber=`echo $fileTime | grep -E '^\-?[0-9]+.?[0-9]*$'` #grep only numbers, if it's not a number isNumber will be empty
            if [ "$isNumber" != '' ]  # Check if $isNumber is NOT empty (which mean that it's a number)
            then
              let "folderTime=$fileTime+$folderTime" #Calculate Total duration in seconds
            fi
        done
        secondToTime $folderTime # Convert seconds to days hours minutes seconds and print it out
    done
done
IFS=$OIFS

관련 정보