LinuxQuestions.org
Review your favorite Linux distribution.
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 11-26-2010, 10:26 PM   #1
kerji
LQ Newbie
 
Registered: Nov 2010
Distribution: Debian 7.5 / Mint 17
Posts: 5

Rep: Reputation: 0
Bash script: Where are these leading spaces coming from?


So I wrote a script to generate a tracklist from a directory of audio files, but it comes out looking like this:
Code:
01 - (00:10:12.731) - Electroplasm
 02 - (00:06:37.690) - Shpongolese Spoken Here
 03 - (00:06:24.287) - Nothing Is Something Worth Doing
 04 - (00:10:25.947) - Ineffable Mysteries
 05 - (00:11:36.581) - I Am You
 06 - (00:08:54.179) - Invisible Man In A Fluorescent Suit
 07 - (00:08:02.118) - No Turn Un-Stoned
 08 - (00:08:13.140) - Walking Backwards Through The Cosmic Mirror
The relevant section of code is:
Code:
## variables
declare -a array
index=0
writefile=0
spacer=" - "

## This is the part that does the work
for i in $filetype
do
        track=$(mediainfo --Inform=General\;%Track/Position% "$i") # track number
                if [[ ${#track} == 1 ]];then    # Makes nice columns. Pads track numbers with leading zero
                        track="0$track";        # Example: 1 becomes 01 and 10 stays 10
                fi
        duration=\($(mediainfo --Inform=General\;%Duration/String3% "$i")\)     # Track length
        title=$(mediainfo --Inform=General\;%Track% "$i")                       # Track title
        array[$index]="$track$spacer$duration$spacer$title\\n"
        let "index+=1"
done
if [[ $writefile == 1 ]]; then                  # -o was set
        echo -e "${array[*]}" >> $filename;     # output to file
else
        echo -e "${array[*]}";                  # output to STDOUT
fi
I can't tell why all lines after the first have a space at the beginning. An older version of my script worked how I expected, but it ran slower because I was echo'ing each result from within the for loop rather than storing the results in an array and writing it all out at once.

This works, but it runs slower:
Code:
for i in $filetype
do
        track=$(mediainfo --Inform=General\;%Track/Position% "$i") # track number
        length=${#track}                        # makes nice columns
                if [[ $length == 1 ]]           # pad track numbers with leading zero
                then                            # example: 1 becomes 01 and 10 stays 10
                        track="0$track";
                fi
        duration=\($(mediainfo --Inform=General\;%Duration/String3% "$i")\)     # Track length
        title=$(mediainfo --Inform=General\;%Track% "$i")                       # Track title
        lineout="$track$spacer$duration$spacer$title"
                if [[ $writefile == 1 ]]; then          # -o was set
                        echo $lineout >> $filename;     # output to file
                else                                    # -o was not set
                        echo $lineout;                  # output to STDOUT
                fi
done

Last edited by kerji; 11-26-2010 at 10:28 PM.
 
Old 11-26-2010, 10:42 PM   #2
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Debian
Posts: 8,577
Blog Entries: 31

Rep: Reputation: 1195Reputation: 1195Reputation: 1195Reputation: 1195Reputation: 1195Reputation: 1195Reputation: 1195Reputation: 1195Reputation: 1195
I can't understand why it is happening by inspection; you may like to add some debugging lines to see where it is coming from:
Code:
for i in $filetype
do
        track=$(mediainfo --Inform=General\;%Track/Position% "$i") # track number
                if [[ ${#track} == 1 ]];then    # Makes nice columns. Pads track numbers with leading zero
                        track="0$track";        # Example: 1 becomes 01 and 10 stays 10
                fi
        echo "DEBUG: \$track is '$track'"
        duration=\($(mediainfo --Inform=General\;%Duration/String3% "$i")\)     # Track length
        title=$(mediainfo --Inform=General\;%Track% "$i")                       # Track title
        array[$index]="$track$spacer$duration$spacer$title\\n"
        echo "DEBUG: \$array[$index] is '${array[$index]}'"
        let "index+=1"
done
Incidentally, unless you may prefer loading the array with array+=( "$track$spacer$duration$spacer$title\\n" ) which allows you to dispense with $index.

EDIT: nice taste in music, by the way

Last edited by catkin; 11-26-2010 at 10:48 PM.
 
Old 11-26-2010, 10:55 PM   #3
kerji
LQ Newbie
 
Registered: Nov 2010
Distribution: Debian 7.5 / Mint 17
Posts: 5

Original Poster
Rep: Reputation: 0
new output
Code:
DEBUG: $track is '01'
DEBUG: $array[0] is '01 - (00:10:12.731) - Electroplasm
'
DEBUG: $track is '02'
DEBUG: $array[1] is '02 - (00:06:37.690) - Shpongolese Spoken Here
'
DEBUG: $track is '03'
DEBUG: $array[2] is '03 - (00:06:24.287) - Nothing Is Something Worth Doing
'
DEBUG: $track is '04'
DEBUG: $array[3] is '04 - (00:10:25.947) - Ineffable Mysteries
'
DEBUG: $track is '05'
DEBUG: $array[4] is '05 - (00:11:36.581) - I Am You
'
DEBUG: $track is '06'
DEBUG: $array[5] is '06 - (00:08:54.179) - Invisible Man In A Fluorescent Suit
'
DEBUG: $track is '07'
DEBUG: $array[6] is '07 - (00:08:02.118) - No Turn Un-Stoned
'
DEBUG: $track is '08'
DEBUG: $array[7] is '08 - (00:08:13.140) - Walking Backwards Through The Cosmic Mirror
'
01 - (00:10:12.731) - Electroplasm
 02 - (00:06:37.690) - Shpongolese Spoken Here
 03 - (00:06:24.287) - Nothing Is Something Worth Doing
 04 - (00:10:25.947) - Ineffable Mysteries
 05 - (00:11:36.581) - I Am You
 06 - (00:08:54.179) - Invisible Man In A Fluorescent Suit
 07 - (00:08:02.118) - No Turn Un-Stoned
 08 - (00:08:13.140) - Walking Backwards Through The Cosmic Mirror
It looks like the linebreak at the end of each entry is causing the headache.

If I change it to
Code:
array+=( "$track$spacer$duration$spacer$title" )
How should I modify the part where I write the final result to include a line break? Simply removing the \n gives me this
Code:
...
...
DEBUG: $array[6] is '07 - (00:08:02.118) - No Turn Un-Stoned'
DEBUG: $track is '08'
DEBUG: $array[7] is '08 - (00:08:13.140) - Walking Backwards Through The Cosmic Mirror'
01 - (00:10:12.731) - Electroplasm 02 - (00:06:37.690) - Shpongolese Spoken Here 03 - (00:06:24.287) - Nothing Is Something Worth Doing 04 - (00:10:25.947) - Ineffable Mysteries 05 - (00:11:36.581) - I Am You 06 - (00:08:54.179) - Invisible Man In A Fluorescent Suit 07 - (00:08:02.118) - No Turn Un-Stoned 08 - (00:08:13.140) - Walking Backwards Through The Cosmic Mirror
Edit: I like the simplicity of array+, but I'm not fully sure how to use it (making the debug output, for example.) What's the name of that operation, so I can look it up?

Last edited by kerji; 11-26-2010 at 11:03 PM.
 
Old 11-26-2010, 11:26 PM   #4
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Debian
Posts: 8,577
Blog Entries: 31

Rep: Reputation: 1195Reputation: 1195Reputation: 1195Reputation: 1195Reputation: 1195Reputation: 1195Reputation: 1195Reputation: 1195Reputation: 1195
Thanks for the update

Omitting the \\n from array+=( "$track$spacer$duration$spacer$title" ) was accidental (but turns out to be useful!).

+= is documented in Shell Parameters. Array loading by =( <list> ) is documented in Arrays.

The debug lines identified the final output as the problem area. The problem is in the expansion of ${array[*]} which the shell renders as a space-separated list of words. Strictly it's not space-separated but separated by the first character of IFS. This leads to a solution by IFS=$'\n \t' combined with not having a newline character at the end of each array element value. This solution makes echo's -e option unnecessary.

To avoid having to set and reset IFS the Simple Command Expansion's temporary environment variable (my terminology) facility can be used:
Code:
IFS=$'\n \t' echo "${array[*]}"
 
Old 11-26-2010, 11:49 PM   #5
kerji
LQ Newbie
 
Registered: Nov 2010
Distribution: Debian 7.5 / Mint 17
Posts: 5

Original Poster
Rep: Reputation: 0
Ah... I was just reading about IFS, but hadn't yet figured out how to use it. Thanks for being so helpful. Thank you for the links too.
 
Old 11-27-2010, 12:14 AM   #6
grail
LQ Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 9,619

Rep: Reputation: 2940Reputation: 2940Reputation: 2940Reputation: 2940Reputation: 2940Reputation: 2940Reputation: 2940Reputation: 2940Reputation: 2940Reputation: 2940Reputation: 2940
Well I must have missed something because no amount of changing IFS has allowed me to print the items either on separate lines or with no extra space after the first line
Of course all is solved if array is fired through a loop, but a simple echo does not seem to cut it.
 
Old 11-27-2010, 01:46 AM   #7
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Debian
Posts: 8,577
Blog Entries: 31

Rep: Reputation: 1195Reputation: 1195Reputation: 1195Reputation: 1195Reputation: 1195Reputation: 1195Reputation: 1195Reputation: 1195Reputation: 1195
Quote:
Originally Posted by grail View Post
Well I must have missed something because no amount of changing IFS has allowed me to print the items either on separate lines or with no extra space after the first line
I was wrong about the "temporary environment variable" technique (and do not understand why yet) but an ordinary environment variable works:
Code:
c@CW8:~$ a=( 1 2 3 4 )      
c@CW8:~$ IFS=$'\n' echo "${a[*]}"
1 2 3 4
c@CW8:~$ export IFS=$'\n'               
c@CW8:~$ echo "${a[*]}"
1
2
3
4
c@CW8:~$ bash --version
GNU bash, version 4.1.7(2)-release (x86_64-slackware-linux-gnu)
 
Old 11-27-2010, 04:06 AM   #8
grail
LQ Guru
 
Registered: Sep 2009
Location: Perth
Distribution: Manjaro
Posts: 9,619

Rep: Reputation: 2940Reputation: 2940Reputation: 2940Reputation: 2940Reputation: 2940Reputation: 2940Reputation: 2940Reputation: 2940Reputation: 2940Reputation: 2940Reputation: 2940
The other shortest solution I came up with for the script is to use tr:
Code:
echo "${array[*]}" | tr ' ' '\n'
Which I probably prefer if unable to play with IFS inline.
 
Old 11-27-2010, 05:11 AM   #9
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Debian
Posts: 8,577
Blog Entries: 31

Rep: Reputation: 1195Reputation: 1195Reputation: 1195Reputation: 1195Reputation: 1195Reputation: 1195Reputation: 1195Reputation: 1195Reputation: 1195
Quote:
Originally Posted by catkin View Post
I was wrong about the "temporary environment variable" technique (and do not understand why yet)
Seems it only works for external commands, not for builtins:
Code:
c@CW8:~$ /bin/env | grep USER
USER=c
c@CW8:~$ USER=x /bin/env | grep USER
USER=x
c@CW8:~$ USER=x builtin set | grep USER
USER=c
That is not clear from the bash man page which says "Otherwise, the variables are added to the environment of the executed command and do not affect the current shell environment" and elsewhere uses the term command for both builtins and externals.
 
  


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
[SOLVED] bash script trying to remove a leading ' and a tailing ' spartiati Programming 25 03-30-2010 03:33 AM
I question on a bash script and filename spaces... trist007 Programming 13 01-17-2010 06:13 AM
ps x without leading spaces LocoMojo Programming 3 03-09-2007 06:22 PM
bash script with spaces Quantum0726 Programming 2 11-14-2005 09:26 PM
bash script ? -- spaces in passed parameters azwr Linux - Newbie 3 06-18-2004 06:57 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 11:17 AM.

Main Menu
Advertisement
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
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration