grepコマンドを使用してファイルから日付全体を抽出できますか?

grepコマンドを使用してファイルから日付全体を抽出できますか?

grepを使用してLinuxシステム上のファイルからパーティションの日付/時刻を抽出するのに役立ちます。

ソースファイルは、次のデータを含むXMLです。

<item start="20231010073000 +0100" stop="20231010100000 +0100">...</item>

完全な開始日を抽出する必要がありますが、grepを使用すると完全な結果を得ることはできません。私のコード:

for startDate in $(grep -Eo 'start="[0-9]{14} [\+|\-][0-9]{4}"' "$filepath" ); do
  echo "$startDate"
done

2つの異なる結果を得る。

start="20231010073000
+0100"

次のように取得できます。

start="20231010073000 +0100"

私は他の例を試しましたが、\s同じ[[:space:]]解決策を使用しました。

私のコードにバグがあるようですが、修正することはできません。

どんな助けでもくれてありがとう!

答え1

grep使用またはregex解析しないでくださいHTML/XML生のテキスト行を処理するように設計されたツールを使用すると、構造化テキスト(XML / HTMLなど)を解析することはできず、解析することもできません。 XML/HTML を処理する必要がある場合は、XML/HTML パーサーを使用してください。ほとんどの言語にはXML解析サポートが組み込まれており、たとえばコマンドラインシェルですばやく作業を行う必要がある場合などの特殊なツールがありますxidel。アクセスできない場合は、操作を受け入れないでください。適切なツール。xmlstarletxmllint


最も先進的なコマンドラインツールは次のとおりです。xidel。構文はorよりも直感的でモダンです(XPath3他のツールが制限されている場合もサポートXPath1)。以下を参照してください。xmlstarletxmllint

xidel -e '//item/@start' -s file.xml
20231010073000 +0100
  • -eXPath e表現に使用される
  • -silent の場合s(状態情報なし)

クエリ言語は、XPathXML / HTMLを解析するさまざまな状況で役立ちます。


XPath指導時間:

https://developer.mozilla.org/en-US/docs/Web/XPath
http://www.w3schools.com/xpath/xpath_functions.asp
http://stackoverflow.com/tags/xpath/info
https://topswagcode.com/xpath/(インタラクティブXPathゲーム、基本が揃っていてインタラクティブに練習したいとき)

答え2

問題はループにあります。デフォルトでは分割されます$IFS(デフォルトはランダムまたは$IFS文字シーケンスで、最初と最後も削除します)。spacetabnewline

この問題を解決するには、次のようないくつかの方法があります。

while IFS= read -r StartDate; do
    echo "$StartDate"
done < <(grep -Eo -- 'start="[0-9]{14} [+-][0-9]{4}"' "$filepath")

(オプションが有効になっていない場合は、bashシェルの場合と同様に、ループがサブシェルではなく現在のシェルになるようにするために:代わりに:loop < <( command generating the input )型を使用します。常に必要なわけではありません。値はサブシェルの末尾から消え、現在のシェルから取得できません。command generating the input | looplastpipe$StartDate

答え3

XMLを扱っているので、実際に属性値を取得するにはXMLパーサーを使用する必要があります。

start次のコマンドを使用して文書全体のすべてのノードから属性値を取得する方法は次のとおりです。itemxmlstarlet

$ xmlstarlet select --template --value-of '//item/@start' --nl file
20231010073000 +0100

または、省略されたオプション名を使用してください。

$ xmlstarlet sel -t -v '//item/@start' -n file
20231010073000 +0100

複数のノードがあり、最初のノードの属性値のみがitem必要な場合は、XPathクエリでこれを使用します。start//item[1]/@start

その後、標準コマンド置換を使用して結果を変数として転送できます。

start=$( xmlstarlet sel -t -v '//item[1]/@start' file )

-nこれ以上不要になったため、上記のコマンドからそのオプションを削除しました。出力の末尾に改行文字を追加しますが、コマンドの置き換えはそれを削除します。)

bashまたは、以下を使用してすべての項目を配列として読み取ることができますreadarray

readarray -t startarray < <(
    xmlstarlet sel -t -v '//item/@start' -n file
)

その後、(または)for start in "${startarray[@]}"; do ...; done出力を直接繰り返します。xmlstarlet

while IFS= read -r start; do
   # ...
done < <( xmlstarlet ...as above... )

答え4

XMLを正しく解析するためにシステムに追加の依存関係をインストールできない場合は、1行で実行するのではなく、解析をよりエレガントに処理するスクリプトを作成します。

以下は、あなたが提供した行からその時間を解析するサンプルスクリプトです。

#!/usr/bin/env bash

INPUT_FILE="$1"
TIME_FILTER='[0-9]*\s(\+|\-)[0-9]*'

__getStart(){
  line="$1"
  echo "$line" | egrep -o "start=\"${TIME_FILTER}\"" | egrep -o "$TIME_FILTER"
}

__getStop(){
  line="$1"
  echo "$line" | egrep -o "stop=\"${TIME_FILTER}\""  | egrep -o "$TIME_FILTER"
}
  

while IFS= read -r line; do
    start_time="$(__getStart "$line")"
    stop_time="$(__getStop "$line")"
    echo "Start Time: ${start_time}"
    echo "Stop Time: ${stop_time}"
done < "$INPUT_FILE"

このようにスクリプトを使用できます

[/var/tmp] $ ./get-dates.sh date-extraction.xml 
Start Time: 20231010073000 +0100
Stop Time: 20231010100000 +0100

関連情報