Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

After some manipulations, I got the file with commands that I would be glad to be able to execute.

Input file (name of file is inp2.txt):

"02 - Beautiful Emptiness.mp3"
"02 - Come. mp3"
"02 - Go For It.mp3"

Code:

#!/bin/bash
exec 4<inp2.txt           # opening  file via descriptor
while read LINE <&4; do
    printf "%s\n" "$LINE"  # just to watch that command is proper
    $LINE                # execute command
done

EDIT: sorry, I omitted important part of code that I thought is not important for understanding. Full code:

#!/bin/bash
IFS='
 '
declare LINE
declare BUF
declare a
a=1 

rm inp2.txt # delete previous version of file
exec 3< inp.txt # open descriptor for a file()content of it is written above
while read LINE <&3; do  # read it
    printf "mv ""$LINE"" %s\n" """$a"".mp3" >> inp2.txt 
    # form a command and output it to the file, just to make sure that I got a command that I really want
    let "a++"  # increment number that will be a part of new unique name
done

exec 4<inp2.txt #open again descriptor with ready commands

while read LINE <&4; do
    printf "%s\n" "$LINE"  # check again
    $LINE #  here it should be executed, but I get mistakes that  is pointed down
done

exit 0

Output:

enter image description here

It seems that command is proper, but something is wrong and I can't figure out what's wrong exactly.

share|improve this question

migrated from serverfault.com Aug 30 '12 at 0:11

This question came from our site for professional system and network administrators.

    
For what it's worth, if this script is purely to rename files, there's already many very powerful tools out there to do such a thing. –  Sirex Aug 31 '12 at 3:23
    
interesting, are there such tools, if for example I want to give new names starting from zero to N and simultaneously be free from renaming them manually? –  gekannt Sep 1 '12 at 9:44
add comment

5 Answers 5

up vote 7 down vote accepted

The script, data file and output that you posted are inconsistent. Neither the script not the data file contain mv, yet your screenshot does. Also, your screenshot mentions a line 28, which the script you posted doesn't have. It's difficult to pinpoint your problem when you give us inconsistent information.

That said, you're trying to do one of two things, neither of which can work the way you're trying.

  • If the input file contains lines like

    mv "02 - Beautiful Emptiness.mp3" 1.mp3
    

    then it's really a shell script. Instead of reading it line by line, execute it as a shell script. Make sure that you can trust this file, since you'll be executing whatever is in there, including rm -rf ~ or some such.

    . inp2.sh
    
  • If the input file contains lines like

    "02 - Beautiful Emptiness.mp3"
    

    then the way you're reading it doesn't work. read LINE does the following:

    • read one line;
    • if that line ends with a backslash, remove the backslash and read another line (repeat until a line that doesn't end with a \ has been read);
    • replace all backslash+character sequences by the second character only;
    • set LINE to the concatenation of the lines read, minus the newlines.

    When the shell executes the command $LINE, it does what it always does when it sees a variable substitution outside quotes, which is:

    • split the value of the variable into a list of words at every place where it contains whitespace (assuming the default value of IFS);
    • treat each word as a glob pattern, and expand it if it matches at least one file.

    Sounds useless? It is. And note that there's nothing about quotes in here: quotes are part of the shell syntax, they aren't part of the shell expansion rules.

What you probably should to is have inp2.txt contain a list of file names, one per line. See Why is `while IFS= read` used so often, instead of `IFS=; while read..`? for how to read a list of lines from a file. You'll be wanting something like

i=1
while IFS= read -r source; do
  dir=$(dirname -- "$source")
  ext=
  case "${source##*/}" in
    *.*) ext=.${source##*.};;
  esac
  mv -- "$source" "$dir/$i$ext"
done <inp2.txt

Just for completeness, I'll mention another possibility, but I don't recommend it because it's fiddly and it won't let you do what you seem to be doing. A file like

"02 - Beautiful Emptiness.mp3" "02 - Come. mp3" foo\ bar.mp3

then it can be read by the xargs command. The input to xargs is a whitespace-delimited list of elements, which can be either a literal (possibly containing whitespace) surrounded by single quotes, a literal (possibly containing whitespace) surrounded by double quotes, or an unquoted literal which may contain backslash escapes (\ quotes the next character). Note that the xargs syntax is unlike anything the shell might recognize.

share|improve this answer
    
thanks! I lost important part of code, I'd edited it and added full initially code –  gekannt Aug 30 '12 at 1:52
add comment

Did you try something like this?

while read LINE <&4; do
    printf "%s\n" "$LINE"  # just to watch that command is proper
    comm="$LINE"                # asssign command to a variable
    $comm                  # execute command
done

It worked for me using bash.

share|improve this answer
add comment

Thank you for responses and patience to me. The main conclusion to that I've come after reading of answers is that I should improve my bash skills.

But I was need quick solution (because it's only auxiliary part of a bigger project).

So, my solution is:

  #!/bin/bash
  ls *.mp3   > inp.txt  # input variables were input once

  declare LINE
  declare a
  a=1 

  rm inp2.txt
  exec 3< inp.txt
  while read LINE <&3; do
   printf "mv  "" '$LINE' "" %s\n"   "'$a.mp3'" >> inp2.txt
   let "a++"
  done



  exit 0

After that I couldn't allow myself to rewrite whole code, so I've used Python:

  #!/usr/bin/python
  # -*- coding: utf-8 -*-

  import os 
  f=open("inp2.txt","r")  # just read command from file and execute them
  for line in f:
   print line 
   os.system(line)

In any case I don't pretend that this can be a solution (I put shamefully my head into sand and I understand that it's a bad solution).

Just let it be here, perhaps it'll be useful for someone trying to use Bash after another interpreted languages like me.

share|improve this answer
add comment

This version works by constructing command lines and piping them into bash for execution.

#! /bin/bash 

i=1
while read LINE ; do
  # strip off any leading or trailing double-quotes.
  # script now works whether input quotes the filenames or not.
  LINE=$(echo "$LINE" | sed -r -e 's/^"|"$//g')
  [ -n "$LINE" ] && echo mv \"$LINE\" \"$i.mp3\"
  i=$((i + 1)) 
done < inp2.txt | bash

NOTE: to dry-run test the script, either delete the | bash from the final line or change it to | cat, which produces output like this:

mv "02 - Beautiful Emptiness.mp3" "1.mp3"
mv "02 - Come. mp3" "2.mp3"
mv "02 - Go For It.mp3" "3.mp3"

The [ -n "$LINE" ] test skips any blank lines in the input...added because i created my inp2.txt with an extra empty line at the end.

share|improve this answer
add comment

From the looks of it, you are having problems with quotes.

By wrapping "$LINE" in quotes (on the 'execute command' line), that is treated as a single command, not the command mv with some additional parameters. So for starters, you should remove the quotes in that line.

In addition, you will have problems with your paths: I see that you have filenames with spaces in them. So you will have to modify your inp2.txt file to have quotes around any paths/filenames with spaces in them, or escape the spaces.

In addition, you may want to try adding the following to the top of your script:

IFS='
'

Yes, the second line is just a single quote on that line. This will change how spaces are handled in your script.

Your script would then look like:

#!/bin/bash
IFS='
'
exec 4<inp2.txt           # opening  file via descriptor
while read LINE <&4; do
 printf "%s\n" "$LINE"  # just to watch that command is proper
$LINE                # execute command
done
share|improve this answer
    
Thanks for response ! I've just tried to use quotes paste.org.ru/?hy9kee , still some problems. What can be wrong? Somehow it thinks that second parameter is a directory's name. Source code is changed in question according to your notion –  gekannt Aug 29 '12 at 23:28
    
sorry for my negligence, it's just a part of code (I tried to be as short as possible). I can't understand what you added in addition. –  gekannt Aug 29 '12 at 23:47
    
Well, to add the two lines from my answer directly below your #!/bin/bash line. –  brain99 Aug 29 '12 at 23:48
    
it made some effect: imm.io/CwCM Now I get in the report about error second parameter without quotes. My whole script that I run is paste.org.ru/?zfsybv (it matters) –  gekannt Aug 29 '12 at 23:56
    
Why do you first write the commands to a file instead of directly executing them in your first loop? Anyway, upon seeing your entire script I believe the IFS addition does more harm than good, so you better remove it again. –  brain99 Aug 30 '12 at 0:07
show 1 more comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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