LinuxQuestions.org
Help answer threads with 0 replies.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices

Reply
 
Search this Thread
Old 02-20-2013, 01:52 PM   #1
stf92
Senior Member
 
Registered: Apr 2007
Location: Buenos Aires.
Distribution: Slackware
Posts: 3,125

Rep: Reputation: 46
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?
 
Old 02-20-2013, 02:03 PM   #2
colucix
Moderator
 
Registered: Sep 2003
Location: Bologna
Distribution: CentOS 6.5 OpenSuSE 12.3
Posts: 10,490

Rep: Reputation: 1956Reputation: 1956Reputation: 1956Reputation: 1956Reputation: 1956Reputation: 1956Reputation: 1956Reputation: 1956Reputation: 1956Reputation: 1956Reputation: 1956
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.
 
Old 02-20-2013, 02:26 PM   #3
suicidaleggroll
Senior Member
 
Registered: Nov 2010
Location: Colorado
Distribution: OpenSUSE, CentOS
Posts: 2,747

Rep: Reputation: 977Reputation: 977Reputation: 977Reputation: 977Reputation: 977Reputation: 977Reputation: 977Reputation: 977
Code:
list="$(ls *mp3)"
for file in $list
should be

Code:
for file in *mp3
 
Old 02-20-2013, 02:27 PM   #4
propofol
Member
 
Registered: Nov 2007
Location: Seattle
Distribution: Debian Wheezy & Jessie; Ubuntu
Posts: 267

Rep: Reputation: 51
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
 
Old 02-20-2013, 02:56 PM   #5
stf92
Senior Member
 
Registered: Apr 2007
Location: Buenos Aires.
Distribution: Slackware
Posts: 3,125

Original Poster
Rep: Reputation: 46
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.

Last edited by stf92; 02-20-2013 at 02:58 PM.
 
Old 02-20-2013, 03:24 PM   #6
colucix
Moderator
 
Registered: Sep 2003
Location: Bologna
Distribution: CentOS 6.5 OpenSuSE 12.3
Posts: 10,490

Rep: Reputation: 1956Reputation: 1956Reputation: 1956Reputation: 1956Reputation: 1956Reputation: 1956Reputation: 1956Reputation: 1956Reputation: 1956Reputation: 1956Reputation: 1956
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"
 
1 members found this post helpful.
Old 02-20-2013, 03:49 PM   #7
stf92
Senior Member
 
Registered: Apr 2007
Location: Buenos Aires.
Distribution: Slackware
Posts: 3,125

Original Poster
Rep: Reputation: 46
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.
 
Old 02-20-2013, 10:05 PM   #8
stf92
Senior Member
 
Registered: Apr 2007
Location: Buenos Aires.
Distribution: Slackware
Posts: 3,125

Original Poster
Rep: Reputation: 46
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.
 
Old 02-21-2013, 02:36 AM   #9
grail
Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 7,492

Rep: Reputation: 1891Reputation: 1891Reputation: 1891Reputation: 1891Reputation: 1891Reputation: 1891Reputation: 1891Reputation: 1891Reputation: 1891Reputation: 1891Reputation: 1891
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.

Last edited by grail; 02-21-2013 at 02:37 AM.
 
Old 02-21-2013, 03:42 AM   #10
stf92
Senior Member
 
Registered: Apr 2007
Location: Buenos Aires.
Distribution: Slackware
Posts: 3,125

Original Poster
Rep: Reputation: 46
I like it. It seems more compact.
 
Old 02-21-2013, 03:58 AM   #11
stf92
Senior Member
 
Registered: Apr 2007
Location: Buenos Aires.
Distribution: Slackware
Posts: 3,125

Original Poster
Rep: Reputation: 46
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?
 
Old 02-21-2013, 08:32 AM   #12
grail
Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 7,492

Rep: Reputation: 1891Reputation: 1891Reputation: 1891Reputation: 1891Reputation: 1891Reputation: 1891Reputation: 1891Reputation: 1891Reputation: 1891Reputation: 1891Reputation: 1891
Probably need to investigate the trap and / or wait command.
 
Old 02-21-2013, 08:48 AM   #13
mina86
Member
 
Registered: Aug 2008
Distribution: Slackware
Posts: 374

Rep: Reputation: 150Reputation: 150
Quote:
Originally Posted by propofol View Post
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 View Post
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

Last edited by mina86; 02-21-2013 at 08:54 AM.
 
Old 02-22-2013, 01:06 AM   #14
propofol
Member
 
Registered: Nov 2007
Location: Seattle
Distribution: Debian Wheezy & Jessie; Ubuntu
Posts: 267

Rep: Reputation: 51
Quote:
Originally Posted by mina86 View Post
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'
 
Old 02-22-2013, 08:54 AM   #15
mina86
Member
 
Registered: Aug 2008
Distribution: Slackware
Posts: 374

Rep: Reputation: 150Reputation: 150
Quote:
Originally Posted by propofol View Post
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.
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
php scripts cant locate filenames with spaces latavish Programming 4 04-13-2011 02:55 PM
bash scripting: filenames with spaces joanq Programming 2 05-24-2005 12:50 PM
Spaces in filenames with BASH edenning Programming 12 01-27-2005 07:10 AM
bash and filenames with spaces doctorwebbox Linux - General 2 12-15-2004 08:55 PM
bash, loops and spaces in filenames shy Programming 5 11-08-2004 07:43 AM


All times are GMT -5. The time now is 05:31 AM.

Main Menu
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration