0

This script name is hello

for i in $(ls $*);do
    x=$(echo $(basename $i".md"));
    pandoc $i -t "latex" -o $x.pdf;
done

When I write this

$ ./hello *bye.md

This error

ls: cannot access 'test': No such file or directory  
ls: cannot access 'de': No such file or directory  
ls: cannot access 'bye.md': No such file or directory
basename: extra operand ‘bye.md’  
Try 'basename --help' for more information.  
./hello: 2: ./hello: pandoc: not found  
basename: extra operand ‘.md’  
Try 'basename --help' for more information.  
./hello: 2: ./hello: pandoc: not found  

I dont understand why my script hello doesn't work correctly ?

And the following command does not create pdf in the correct directories:

$ ./hello */test.md  

4 Answers 4

1

You can debug scripts with the command set -x, which shows exactly which commands are being executed (you can disable that mode with set +x). You'll probably see from this that you're running the loop on non-existent files and then running basename incorrectly.

In addition:

  • don't parse the output of ls. for i in "$@"; do will work just fine for you.

  • basename needs the extension as a separate argument, so you should have written basename $i ".md": you were missing that space between the variable holding the filename and the extension.

  • you should quote variables if they contain spaces, so you actually want basename "$i" .md and pandoc "$i" -t "latex" -o "$x".pdf

  • the echo is redundant, you might as well use x=$(basename "$i" .md)

2
  • 2
    Also, to iterate over the arguments, you must use for i in "$@" -- otherwise arguments that contain spaces will be split leading to the OP's error. Dec 11, 2018 at 18:49
  • 1
    for i in "$*" will iterate exactly one time, over the single string that you get from concatenating all the positional parameters with the first character from $IFS as delimiter.
    – Kusalananda
    Dec 11, 2018 at 18:54
0

There are a number of things wrong with the script, that are obvious for the more experienced scripter. But your question was "how do I debug".

First of all: write the script in a legible form instead of doing a one-liner:

#!/bin/bash
for i in $(ls $*) ; do 
    x=$(echo $(basename $i".md"))
    pandoc $i -t "latex" -o $x.pdf
done

You will notice that I added a #!/bin/bash. That forces the use of bash for the script. So, how to debug this?

$i is the loop variable; does it contain what you think when you go through the loop? An echo will make that clear. You could do the same for your $x:

#!/bin/bash
for i in $(ls $*) ; do 
    echo "LOOP VARIABLE $i"
    x=$(echo $(basename $i".md"))
    echo "AND X IS $x"
    pandoc $i -t "latex" -o $x.pdf
done

That is a basic way of debugging.

So, now back to your script, output etc. Am I correct that you have a file called "test de bye.md" in your directory?

OK. Start at the beginning (actually: the missing @! was that but that is nitpicking). Never parse the output of ls. I have been chastised many times for it and here you can read why.

You may also be interested in the difference between $* and $@, which I will leave to your google skills.

The construction x=$(echo $(basename $i".md")) is bizar. I do not even understand what you are trying accomplish here, especially if you are already calling the script with the .md at the end of the filename.

You may also want to quote arguments that have spaces in them. For example: if $i would contain test de bye.md, ls $i would call `ls with 3 arguments:

  • test
  • de
  • bye.md

while ls "$i" would cal ls with one argument:

  • test de bye.md

which will have completely different results.

Another question: are you sure that pandoc is installed on your system? The error message suggests it is not. Verify with which pandoc.

For those who did Fortran (and I am that old!), i is an integer. But you might as well use descriptive names, which will make maintenance of scripts easier late on.

So, that would make the script:

#!/bin/bash
for infile in "$@" ; do
    outfile=${infile%.md}.pdf
    pandoc "$infile" -t latex -o "$outfile"
done

Or something like that.

0

Your loop:

for i in $(ls $*);do
    x=$(echo $(basename $i".md"));
    pandoc $i -t "latex" -o $x.pdf;
done

Corrected version:

#!/bin/sh

for pathname do
    pandoc "$pathname" -t "latex" -o "$( basename "$pathname" .md ).pdf"
done

Notes:

  • To iterate over the command line arguments, just do for variable do ...; done. $* is the positional parameters joined up with the first character of $IFS, and you very rarely need to use it. Since it's unquoted, the result would have been split on whitespaces, which is the cause of some of the error messages that you get (the ones from ls). The split up words would also undergo filename globbing. The output of ls is strictly for looking at (and there's no reason to list the filenames anyway as they are handed to you on the command line).

  • $(echo $(some-utility)) is more correctly written as $(some-utility). The echo is not needed and will introduce interesting effects in some cases when the given utility output backslashes etc. Also, word splitting and filename globbing would have happened on the output.

  • I've opted for not using a intermediate variable. You just use it once anyway. I also guessed that you wanted to delete the .md filename suffix, and I used a more descriptive variable name for the loop. Note that you seem to be writing to files in the current directory, even though you may be given pathnames that may contain subdirectories, such as somedir/file.md.

Variant that writes to files in the same directory where the original .md file is located:

#!/bin/sh

for pathname do
    pandoc "$pathname" -t "latex" -o "${pathname%.md}.pdf"
done
-1

Using for to loop program output, can be hard work. A while loop tends to be easier. Also, I don't think line 2 is doing what you think it is. Try this instead:

ls $* | while read infilename; do
    outfilename=$(echo $infilename | sed 's/.md/.pdf/')
    pandoc $infilename -t "latex" -o ${outfilename}.pdf;
done
1
  • I didn't downvote, but you've broken the original in new and interesting ways :) Files with .md in the middle (like foo.md.1.md) won't work, and the outputs are now placed in the same directory as the .md files instead of the working directory (which may be desirable, but is different). Also, you could use bash substitution: -o ${infilename%.md}.pdf (or ${infilename/.md/.pdf} to keep the behavior as written) instead of spawning a subshell just to run echo and sed.
    – user4443
    Dec 11, 2018 at 19:03

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .