LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (http://www.linuxquestions.org/questions/programming-9/)
-   -   Filenames with spaces in bash scripts. (http://www.linuxquestions.org/questions/programming-9/filenames-with-spaces-in-bash-scripts-4175450995/)

stf92 02-20-2013 02:52 PM

Filenames with spaces in bash scripts.
 
Code:

#list.sh

#!/bin/sh

list="$(ls *mp3)"
n=0
for file in $list
do
        v[n]="$file"
        let n=n+1
done

n=0
until [ -z "$1" ]
do
        echo "${v[$1]}"
        shift
let n=n+1
done

exit

This script takes an argument n and prints the name of the n+1 th file in the current directory. If given several arguments, for example
Code:

$ list.sh 0 3 4
it prints the 1st, 4th and 5th filename in the directory. The problem is that there may be spaces in the filenames. If the 1st file is "john smith", then 'list.sh 1' will not print the 2nd file name but "smith". The problem is in the first half of the script, the repeat block can be disregarded. How can I solve this?

colucix 02-20-2013 03:03 PM

I think you should definitively read these:
http://mywiki.wooledge.org/BashPitfa...8ls_.2A.mp3.29
http://mywiki.wooledge.org/ParsingLs
http://mywiki.wooledge.org/BashFAQ/020
The first two articles explain what and why is wrong. The last one proposes solutions.

suicidaleggroll 02-20-2013 03:26 PM

Code:

list="$(ls *mp3)"
for file in $list

should be

Code:

for file in *mp3

propofol 02-20-2013 03:27 PM

One option may be to read the list of files directly into an array with one file per line:

Code:

IFS=$'\n'
list=($(ls -1 *.mp3))
# or use $(find ~/mp3folder/ -iname '*.mp3' -print) for a recursive search
IFS=''

n=0
for file in "${list[@]}"
  do
  echo "$file"
  ((n++))
done

Regards,
Stefan

stf92 02-20-2013 03:56 PM

Thank you very much. The links solved the problem. Could I ask you how to determine, within a bash script, if a certain string contains a given substring. For example, given "superior" I want to know it if contains "upe", which here is certainly true.

More precisely, the problem is this: one of the arguments can be of the form m-n, for example,
Code:

list.sh 2 4 6-8
The program would have to know the last argument contains a hyphen (it means play from 6 up to 8) and get the 6 and the 8. Is this very difficult? Once the presence of the hyphen is detected, perhaps sed could extract m and n.

colucix 02-20-2013 04:24 PM

You can try the pattern matching operator =~. In order to match a numeric interval, looking for an hyphen is not enough, otherwise strings like "-8" or "hello-world" or "6-" would be valid. Instead you have to match the numbers as well:
Code:

[[ $1 =~ ^[0-9]+-[0-9]+$ ]] && echo "$1 is a numeric interval"

stf92 02-20-2013 04:49 PM

Cool! Then I could do
Code:

s1=$(echo $1|sed s/"-"/" ")
Having now a blank separator between the two numbers I guess it would be easy to put each one in a separate variable, though I do not immediately see how to do it.

stf92 02-20-2013 11:05 PM

I did it this way:
Code:

# play.sh

# Example: play.sh 0 3-5 8
# Play 1st, 4th, 5th, 6th and 9th files in current directory.
#!/bin/sh

n=0
for file in *mp3
do
        v[n]="$file"
        let n=n+1
done

n=0
until [ -z "$1" ]
do
       
        if [[ $1 =~ ^[0-9]+-[0-9]+$ ]]  # Thanks colucix
        then
                # $1 is an interval m-n
                int=$1
               
                # Split "m-n" in "m" and "n"
                n1="${int%-*}"    # Get m
                n2="${int##*-}"  # Get n
               
                # Process from v[n1] up to v[n2]
                for (( i=$n1; i <= $n2; i++ )); do
                        mplayer "${v[i]}"
                done
        else
                mplayer "${v[$1]}"
        fi
        shift
        let n=n+1
done

exit

It works.

grail 02-21-2013 03:36 AM

Now you have a solution .. here is another way to think about :)
Code:

#!/bin/bash

files=( *.mp3 )

for f
do
        if [[ $f =~ - ]]
        then
                for (( i = ${f%-*}; i < ${f#*-}; i++ ))
                do
                        mplayer "${files[i]}"
                done

                f=${f#*-}
        fi

        mplayer "${files[f]}"
done

I would probably add in a test to make sure that the directory where you run this actually has mp3 files, ie test if array empty.

Many others of course ... just thought it might interest you.

stf92 02-21-2013 04:42 AM

I like it. It seems more compact.

stf92 02-21-2013 04:58 AM

If I press ^C while the script is running it has no other effect than making mplayer go on to the next song. How could I stop the program in response to ^C or some other key?

grail 02-21-2013 09:32 AM

Probably need to investigate the trap and / or wait command.

mina86 02-21-2013 09:48 AM

Quote:

Originally Posted by propofol (Post 4896211)
One option may be to read the list of files directly into an array with one file per line:

Code:

IFS=$'\n'
list=($(ls -1 *.mp3))
# or use $(find ~/mp3folder/ -iname '*.mp3' -print) for a recursive search
IFS=''

n=0
for file in "${list[@]}"
  do
  echo "$file"
  ((n++))
done


No. This is incorrect. File names can have new line characters in them.

Quote:

Originally Posted by stf92 (Post 4896225)
More precisely, the problem is this: one of the arguments can be of the form m-n, for example,
Code:

list.sh 2 4 6-8

Code:

foo=6-8
range=false
case $foo in
*-*-*)
    echo "$foo: invalid argument" >&2
    exit 1
    ;;
?*-?*)
    foo1=${foo%-*}
    foo2=${foo#*-}
    ;;
*-*)
    echo "$foo: invalid argument" >&2
    exit 1
esac

if $range; then
    : use $foo1 and $foo2
else
    : use $foo
fi


propofol 02-22-2013 02:06 AM

Quote:

Originally Posted by mina86 (Post 4896708)
No. This is incorrect. File names can have new line characters in them.

You are correct but how many files do you have on your system with '\n' in the name? If you want to be pedantic: find with '-print0' & IFS=$'\0'

mina86 02-22-2013 09:54 AM

Quote:

Originally Posted by propofol (Post 4897242)
You are correct but how many files do you have on your system with '\n' in the name?

Irrelevant. Assuming that files with new line characters do not exist is incorrect and may lead to security bugs.


All times are GMT -5. The time now is 10:59 PM.