4

It comes down to being able to save the dynamic output (I'll explain) of terminal into a text file, but here's what I'm ultimately seeking.

I created a custom command for terminal called playRandom, what it does is that it plays random songs forever. The bash file I created for this:

#!/bin/bash 
find ./ -type f | sort -R | xargs -I + play +

Note: The play command is from SoX software.

Now the output looks something like this:

playRandom command

As you can see the output changes dynamically, so I cannot use >> to save the output.

I want to be able to save 'names of songs that are played' into a text file.

How can I achieve this goal? Thanks in advance.

4
  • Does play exit right after file is done if you play just one file ? Jun 3 '17 at 20:38
  • @SergiyKolodyazhnyy Yes, but it exits when the song is finished playing. Jun 3 '17 at 20:40
  • @AmirA.Shabani perfect ! I think i can make it work such that it writes out each filename to file before playing. I'll work on a solution, probably will take me 15 -20 minutes. I'll let you know once i post Jun 3 '17 at 20:48
  • @AmirA.Shabani OK, I've posted an answer, covered two cases - outputting currently playing file and outputting whole list before playing the list. Let me know if you have any questions. Jun 3 '17 at 21:06
10

Saving filenames that are played currently

Since the play command terminates after playing a single file, what we can do is instead of using xargs and giving play a batch of files, we'll take out each file, one at a time, and echo it to file, and play the file afterwards. The edited script would look like below. Notice that here are added additional options and IFS= read -d'' -r command to deal with filenames in safe manner.

#!/bin/bash 

# create file for playing songs first
echo > playlist.txt

# Now play each song and echo filename to file
find ./ -type f -print0 | sort -z -R | while IFS= read -d '' -r filename
do
    clear
    echo "$filename" >> playlist.txt
    play "$filename"
done

The advantage of this approach is that filenames will go into playlist.txt as they are played, which allows us to track output of the script in real time with something like tail -F playlist.txt.

NOTE: to avoid playlist.txt being listed in find's output change find command like so:

find ./ -type f -not -name "playlist.txt" -print0

Additionally if we want to ensure that only .mp3 files are listed we can do this:

find ./ -type f \( -not -name "playlist.txt" -and -name "*.mp3" \) -print0

Saving list of found files to be played

If our goal is to safe the file list before it is played, there's not a lot of science to it - the find-sort pipeline can be written to file first, and that file can then be fed to play either via xargs or again via while IFS= read -r ; do ... done structure

#!/bin/bash 

find ./ -type f -print0 | sort -z -R > playlist.txt

while IFS= read -d '' -r filename
do
    clear
    play "$filename"
done < playlist.txt
10
  • Your first approach is what I want, thank you :) but there's a problem. Jun 3 '17 at 22:08
  • @AmirA.Shabani sorry, there should have been an extra space after -d option. I'll fix that in a second. Jun 3 '17 at 22:59
  • @AmirA.Shabani I also noticed that the script finds the playlist.txt file, i.e. it finds it's own output.Do you want me to add improvement to script so that the find command ignores playlist.txt ? Jun 3 '17 at 23:01
  • That would be amazing :)) BTW, now the script works and it's exactly what I wanted. Thanks. Jun 3 '17 at 23:27
  • 1
    @AmirA.Shabani you're welcome , and glad I could help. I've added additional notes on filtering the files with find Jun 3 '17 at 23:47
8
find ./ -type f | sort -R | tee text.file | xargs -I + play +  

Also see man tee.

3

Here is my solution:

1. Real time (Currently playing)

After you ran the play command use this:

soxi "$(readlink /proc/`pidof play`/fd/3)" | grep -Po "(?<=Title=).*"

It will returns the currently playing song's name for you and you are able to redirect it to a file using >.

1.1. Notes

/proc/`pidof play`/fd/3
  • is the file descriptor to your current playing track (file).
  • using readlink we get the file name
  • using soxi we get its information
  • using grep we cut the track title

2. Before playing (List all names)

find ./ -type f | sort -R | tee /tmp/play-files | xargs -I + soxi "+"\
| grep -Po "(?<=Title=).*" > list_of_names && xargs < /tmp/play-files\
-I + play "+"

It will create a file named list_of_names in your current directory which contains all track names.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.