私のスクリプトをどのようにデバッグしますか?

私のスクリプトをどのようにデバッグしますか?

このスクリプトの名前はhelloです。

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

私がこれを書くとき

$ ./hello *bye.md

このエラー

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  

私のスクリプトhelloが正しく機能しない理由を理解できませんか?

そして、次のコマンドは正しいディレクトリにpdfを生成しません。

$ ./hello */test.md  

答え1

set -x実行中のコマンドを正確に示すコマンドを使用してスクリプトをデバッグできます。を使用してこのモードを無効にできますset +x。これにより、存在しないファイルでループを実行してbasename誤って実行されることがわかります。

また:

  • lsの出力を解析しないでください。for i in "$@"; doそれはあなたにとてもよく合います。

  • basename拡張子は別の引数として必要なので、次のように書く必要がありますbasename $i ".md"。ファイル名を含む変数と拡張子の間にスペースはありません。

  • 変数にスペースが含まれている場合は、引用符で囲む必要があります。実際に欲しいbasename "$i" .mdpandoc "$i" -t "latex" -o "$x".pdf

  • これはecho重複します。x=$(basename "$i" .md)

答え2

このスクリプトには、経験豊富なスクリプト作成者に明らかに明らかになる多くの問題があります。しかし、あなたの質問は「どのようにデバッグしますか?」です。

まず、1行のテキストではなく、明確で読みやすい形式でスクリプトを作成します。

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

私が追加したことがわかります#!/bin/bash。これにより、bashスクリプトが強制的に使用されます。では、デバッグする方法は?

$iループ変数には、ループを通過するときの考えが含まれていますか?エコはこれをはっきりと示しています。 $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

これがデバッグの基本的な方法です。

これでスクリプト、出力などに戻ります。test de bye.mdディレクトリに「」というファイルがあります。そうですか?

さて、最初から始めます(実際にはこれは欠けていますが、@!問題がある部分です)。私はこれのために何度も叱りlsました。ここ理由を読むことができます。

$*との違いに興味があるかもしれません$@。それはあなたのGoogleの能力に任せます。

建築物がx=$(echo $(basename $i".md"))変です。特に.mdファイル名の末尾ですでにスクリプトを呼び出す場合は、ここで何を達成したいのかわかりません。

スペースを含むパラメーターを引用することもできます。たとえばtest de bye.md、 $i に が含まれている場合、ls $i`ls は 3 つのパラメータとともに呼び出されます。

  • テスト
  • ダック
  • こんにちは。md

ls "$i"校正にパラメータを使用している間ls

  • こんにちはテスト.md

これにより、まったく異なる結果が得られます。

もう一つの質問:pandocシステムにこのソフトウェアがインストールされていますか?エラーメッセージは、これが真ではないことを示します。で確認してくださいwhich pandoc

Fortranを学んだ方(私がそれほど年上ですね!)をご存知の方にはiは整数です。ただし、説明を含む名前を使用することもでき、将来のスクリプトのメンテナンスが容易になります。

したがって、スクリプトは次のようになります。

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

またはそのようなもの。

答え3

あなたのループ:

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

修正バージョン:

#!/bin/sh

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

メモ:

  • コマンドライン引数を繰り返すには、doは最初の文字に関連付けられた位置引数ですfor variable do ...; done。引用されていないため、結果はスペースで区切られます。これは、いくつかのエラーメッセージが表示される理由です。分割単語にもファイル名のグロービングが適用されます。の出力は厳密に次のようになります。$*$IFSlsls見ているat(ファイル名はコマンドラインから渡されるため、ファイル名を一覧表示する理由はありません)。

  • $(echo $(some-utility))より正確にはで書かれています$(some-utility)。不要がecho紹介されます。興味深い場合によっては、特定のユーティリティがバックスラッシュなどを出力すると、これが影響します。また、出力時にトークン化とファイル名のグロービングが発生します。

  • 私は中間変数を使用しないことにしました。とにかく一度だけ使用してください。また、ファイル名のサフィックスを削除したいと推測し、.mdループにはより説明的な変数名を使用しました。サブディレクトリを含めることができるパス名を取得できますが、現在のディレクトリのファイルに書き込むように見えますsomedir/file.md

元のファイルと同じディレクトリにあるファイルに書き込むバリアント.md

#!/bin/sh

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

答え4

ループプログラムの出力操作はfor難しい作業になる可能性があります。屋根がwhileより簡単になる傾向があります。また、私は2行目があなたが思うことをしているとは思いません。この試み:

ls $* | while read infilename; do
    outfilename=$(echo $infilename | sed 's/.md/.pdf/')
    pandoc $infilename -t "latex" -o ${outfilename}.pdf;
done

関連情報