0

I need to create filelist (playlist) for using it in a Docker container. So the path of the file on the host system is not the mounting path. Now I try to fix this, but I have some trouble.

This is my script where I want to add the replacement

tdir="/mountingpoint"

readarray -d $'\0' listing < <(find "${fdir}"/*.mp3 -print0) #for mixing up and write to file listing=( $(shuf -e "${listing[@]}") ) printf "%s\n" "${listing[@]}" > "playlist.m3u"

A common way should be the use of sed but I have some trouble by putting things together. I think I have to modify the script to something like readarray -d $'\0' listing < <(find "${fdir}"/"${d}"/ -name "*.mp3" -print0 -exec sed -i "s/"$fdir"/"$tdir"/" {} \;) but this result in an error sed: -e expression #1, char 9: unknown option to `s' I think the slash in the dir name cause this error. But I also tried to replace the variables with a folder name to check the result, but nothing happened (beside a longer runtime). Nothing means no replacement in the output file.

I hope someone can handle my bad English and give me a hint what I do wrong. Thanks a lot

Gen-Chan
  • 3
  • 1
  • The specific error is because you are trying to use / as the sed pattern delimiter but also have / in the pattern and replacement strings - see for example How to replace a string with a string containing slash with sed?. But there are other issues. – steeldriver Aug 15 '22 at 21:49
  • 1
    It would be best to experiment outside the readarray to get things working first. I don't think you want to use the -exec option to find, this is invoking sed on the files, not the file names. I think you want `find "${fdir}/${d}" -name *.mp3 -print | sed "s=$fdir=$tdir=". I have dropped the -print0 which is good practice but currently confusing. Let us know how you get on and I can write up an answer if no one beats me to it. – icarus Aug 16 '22 at 03:05

2 Answers2

1

As @icarus has pointed out, the -exec option doesn't make sense here - you want to apply your sed substitution to the null-delimited filenames, using a sed pattern delimiter that is unlikely to be present in those names:

find "${fdir}/${d}" -name '*.mp3' -print0 | sed -z "s:$fdir:$tdir:"

IMHO there's no reason not to include the shuffle inside the pipeline as well:

find "${fdir}/${d}" -name '*.mp3' -print0 | sed -z "s:$fdir:$tdir:" | shuf -z

So for example given

~$ find . -name '*.mp3'
./dir/1.mp3
./dir/2.mp3
./dir/3.mp3
./dir/4.mp3
./dir/5.mp3

then

~$ fdir=./dir; tdir='./new/dir'

~$ readarray -t -d '' listing < <(find . -name '*.mp3' -print0 | sed -z "s:$fdir:$tdir:" | shuf -z)

~$ printf '%s\n' "${listing[@]}" ./new/dir/3.mp3 ./new/dir/1.mp3 ./new/dir/4.mp3 ./new/dir/2.mp3 ./new/dir/5.mp3

steeldriver
  • 81,074
  • Works absolutely fine, and thanks a lot for the improvement of the shuffle. Nice to found a community kind and helpful like this. Thanks, I'm really happy now :-) – Gen-Chan Aug 16 '22 at 17:03
1

With zsh instead of bash:

() {print -rC1 -- $tdir/$^@} $fdir/*.mp3(Noe['REPLY=$RANDOM']:t)

Or assuming neither $fdir nor $tdir contain : characters:

print -rC1 -- $fdir/*.mp3(Noe['REPLY=$RANDOM']:s:$fdir:$tdir)

With bash 4.4+ and GNU shuf (or sort -zR), you can always do:

print0()  { [ "$#" -eq 0 ] || printf '%s\0' "$@"; }
println() { [ "$#" -eq 0 ] || printf '%s\n' "$@"; }

readarray -td '' listing < <( shopt -s nullglob cd -P -- "$fdir" && print0 *.mp3 | shuf -z) println "${listing[@]/#/$tdir}"

Or:

print0()  { [ "$#" -eq 0 ] || printf '%s\0' "$@"; }
println() { [ "$#" -eq 0 ] || printf '%s\n' "$@"; }

readarray -td '' listing < <( shopt -s nullglob print0 "$fdir"/*.mp3 | shuf -z) println "${listing[@]/#"$fdir"/$tdir}"

as an equivalent.