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.

Amir Shabani
  • 239
  • 2
  • 13

3 Answers3

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
Sergiy Kolodyazhnyy
  • 103,293
  • 19
  • 273
  • 492
  • Your first approach is what I want, thank you :) but there's a [problem](http://imgh.us/Screenshot_from_2017-06-04_02-37-01.png). – Amir Shabani Jun 03 '17 at 22:08
  • @AmirA.Shabani sorry, there should have been an extra space after `-d` option. I'll fix that in a second. – Sergiy Kolodyazhnyy Jun 03 '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` ? – Sergiy Kolodyazhnyy Jun 03 '17 at 23:01
  • That would be amazing :)) BTW, now the script works and it's exactly what I wanted. Thanks. – Amir Shabani Jun 03 '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` – Sergiy Kolodyazhnyy Jun 03 '17 at 23:47
  • There's one more thing; I want it to write the name when the song is finished playing. Which it does, but now when I press `ctrl-c` (which will not end the loop, but will play another random song), it still writes the name. – Amir Shabani Jun 04 '17 at 05:15
  • Let us [continue this discussion in chat](http://chat.stackexchange.com/rooms/59854/discussion-between-amir-a-shabani-and-sergiy-kolodyazhnyy). – Amir Shabani Jun 04 '17 at 05:19
  • @AmirA.Shabani hi, Amir. Just wanted to check back with you. I'll see what can be done, but the best way probably would be to kill the script by pid or by `pkill -f script_name.sh`. I might work on this on the weekend, and maybe will find a better solution. – Sergiy Kolodyazhnyy Jun 06 '17 at 12:35
  • Nice solution. One small tweak: When you check `-name '*.mp3'`, you don't need to also check `-not -name 'playlist.txt'`. – Paddy Landau Jun 06 '17 at 13:43
  • @PaddyLandau yeah, that was redundant. I'll fix it up when I've time or you can edit my answer if you wish. – Sergiy Kolodyazhnyy Jun 06 '17 at 14:15
8
find ./ -type f | sort -R | tee text.file | xargs -I + play +  

Also see man tee.

waltinator
  • 35,099
  • 19
  • 57
  • 93
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.

Ravexina
  • 54,268
  • 25
  • 157
  • 179