コマンドラインを使用して、列のすべての項目に固定数を掛けます。ただし、その行がコメントでない場合にのみ適用されます。

コマンドラインを使用して、列のすべての項目に固定数を掛けます。ただし、その行がコメントでない場合にのみ適用されます。

努力しています:

  1. 常に0 0 0ファイルの最初の行に追加されます。
  2. 2*piまたは、次の形式に似た形式で3列ファイルの最初の列を掛けます6.2832。ただし、行が数字で始まる場合にのみ適用されます。 2番目と3番目の列はそのまま残ります。
  3. *行が数字で始まらない場合、aはすでにaではない限り、行の先頭に追加されます*。つまり、すでにコメントされていない限り、現在の行をコメントアウトするだけです。

以下はサンプル入力ファイルです。

* radius, section, index
1.12 A 0
2.0 A 1
   * There is white space before this comment
* This is a comment indicating a new section
5 B 0
3.17 B 1
7.3 B 7
This row starts with an alphabet char and should be commented out by the script.
0 C 1
1 C 2

予想される結果は次のとおりです。

0 0 0
* radius, section, index
7.037184 A 0
12.5664 A 1
* There is white space before this comment
* This is a comment indicating a new section
31.416 B 0
19.917744 B 1
45.86736 B 7
* This row starts with an alphabet char and should be commented out by the script.
0 C 1
6.2832 C 2

これまで私がしたこと:

for tempfile in *.txt; do
    echo '0 0 0' > temp
    cat $tempfile >> temp
    awk '{$1*=6.2832}{print}' temp > $tempfile
    #awk '/^(0-9)/{$1*=6.2832}{print}' temp > $tempfile
rm temp
done

しかし、上記の例のユースケースでは、このスクリプトは何を行いますか?

0 0 0
0 radius, section, index
7.03718 A 0
12.5664 A 1
0 There is white space before this comment
0 This is a comment indicating a new section
31.416 B 0
19.9177 B 1
45.8674 B 7
0 row starts with an alphabet char and should be commented out by the script.
0 C 1
6.2832 C 2

PS。 Linuxボックスはスタンドアロンで、mlrルート/管理者の資格情報もありません。

コミュニティのどんな助けにも感謝します。よろしくお願いします。

答え1

POSIX awkを使用してください。

$ cat tst.awk
NR == 1 {
    CONVFMT = "%.17g"
    pi = atan2(0, -1)
    two_pi = 2 * pi
    print 0, 0, 0
}
{
    if ( $1 ~ /^[0-9]/ ) {
        $1 *= two_pi
    }
    else {
        sub(/^[[:space:]]*\*?[[:space:]]*/,"* ")
    }
    print
}

$ awk -f tst.awk file
0 0 0
* radius, section, index
7.03718 A 0
12.5664 A 1
* There is white space before this comment
* This is a comment indicating a new section
31.416 B 0
19.9177 B 1
45.8674 B 7
* This row starts with an alphabet char and should be ignored by the script.
0 C 1
6.2832 C 2

答え2

たぶんawkを使ってすべてのことをすることができます。

awk '
 ( FNR==1 ) { print "0 0 0" } # add a first line containing "0 0 0"
 /^[0-9]/   { $1 *= 6.2832 }
 /^[a-zA-Z]/ { $0="* " $0 } # comment lines that start with a letter
 1          # always true, and no {action} speficied:
            # does the default "print $0" action and thus prints every line
' your_input_file > output_file

答え3

そしてperl

perl -MMath::Trig -pe '
  BEGIN{print "0 0 0\n"; $x = 2 * pi}
  unless (s{^\d[\d.]*}{$& * $x}e || /^\s*\*/) {
    $_ = "* $_";
  }' your-file

ここで、プレフィックスは"* "数字で始まらず、白いs速度も数字で始まらない*

"* "aで始まる行だけをプレフィックスとして使用してください。手紙する:

perl -C -MMath::Trig -pe '
  BEGIN{print "0 0 0\n"; $x = 2 * pi}
  if (/^\pL/) {
    $_ = "* $_";
  } else {
    s{^\d[\d.]*}{$& * $x}ae;
  }' your-file

(ここにオプションを追加し、-CファイルがUTF-8でエンコードされ、ロケールがその文字エンコーディングを使用していると仮定します)ASCIIに限定されません。アルファベット;ただし、Perlは他のスクリプトの計算ではなくASCII 10進数でのみ計算を実行できるため、ASCII 10進数にのみ一致するように代替項目aにフラグを追加します。s{...}{...}ae\d

Math::Trigモジュールがない場合(定数)、要因定義は次のようにpi変更できます。$x

$x = 2 * atan2(0, -1)

またはハードコードしてください:

$x = 6.28318530717958647692

計算を実行するときは、できるだけ高い精度(そして有用性)を使用し、最終結果から数字だけを切り捨てる必要があります。そうしないと、乗算後にエラーが拡大する可能性があります。

たとえば、これらの円周をミクロン単位まで正確に表示するには(数値がメートル単位で表示されると仮定)、次のようにします。

perl -MMath::Trig -pe '
  BEGIN{print "0 0 0\n"; $x = 2 * pi}
  unless (s{^\d[\d.]*}{sprintf "%.6f", $& * $x}e || /^\s*\*/) {
    $_ = "* $_";
  }' your-file

半径10000000メートルの円の場合、係数に基づいて実際の円周を6.2832得ることができます。62.83200062.831853

答え4

タイトルよりも大きなことを処理したいようです。

これは完全なbashスクリプトです。 Ed Mortonの素晴らしい答えに基づいて構築されており、実際には改善することはできません。

特典:

  1. TAUマジックナンバーを変数()で抽出
  2. エラー処理( TODO)
  3. 入力パラメータとオプションの出力パラメータを使用すると、簡単に実行できます。
  4. ファイルが多い場合は進行状況を報告
  5. 間隔問題の防止(NOTEsを参照)
  6. 対話型および独立実行

欠点:

  1. bashいいえsh
  2. 行が多すぎます(コメントがなくても)。
#!/bin/bash

# TODO: Update me for more accuracy
#
TAU=6.2832

# TODO: handle errors
#
trap on_err ERR
on_err() { echo "failed!" >&2; exit 1; }

# awk processing for input files
#
#   For every line starting with a number,
#      multiply the first number by tau (pi * 2)
#
#   For every line not starting with a number,
#      change the line to start with a *
#
#   Finally, print the line with our changes
#
AWK_PROG="{
  if (\$1 ~ /^[[:space:]]*[0-9]/) {
    \$1 *= $TAU
  } else {
    sub(/^[[:space:]]*\\*?[[:space:]]*/, \"* \")
  }
  print
}"

# Process all files in a directory
#
# Simple usage (reads input from /my/input/*.txt)
#
#   process_all_files /my/input
#
# Advanced usage (writes output to /my/output/*.txt)
#
#   process_all_files /my/input /my/output
#
process_all_files() {
  # Arguments
  #
  # First argument is the input directory
  #
  # Second argument (optional) is the output directory
  #   (a tempfile by default)
  #
  local in_dir="$1"
  local out_dir="${2:-$(mktemp -d)}"

  # List all the files in the input directory (save list in tempfile)
  #
  # NOTE: xargs details
  #         https://unix.stackexchange.com/questions/175844/use-basename-in-find-exec
  #
  local file_list="$(mktemp)"
  find "$in_dir" -iname '*.txt' -print0 \
    | xargs -0 -n1 -- basename \
    > "$file_list"

  # Log state
  #
  # Total number of files
  local n=$(awk 'END{print NR}' "$file_list")
  # Current file number
  i=0

  # Define log function to print to stderr
  #
  # NOTE: change log formatting here
  #
  log() { echo "  $i/$n :: $*" >&2; }

  # Log the input directory (and number of files to process)
  log "$in_dir/*.txt"

  # Iterate over the list of files
  #
  # NOTE: using while read avoids word splitting issues
  #         https://www.shellcheck.net/wiki/SC2044
  #
  cat "$file_list" | while read -r file; do
    # Update current file number and log the file
    ((i++))
    log "$file"

    # Do the actual processing
    #
    # 1. Write the first line of the file
    # 2. Process the rest of the file with awk
    #
    # Whitespace alignment for clarity:
    #   First line truncates the output file if it already exists
    #   Second line appends to the file
    #
    echo 0 0 0           >  "$out_dir/$file"
    awk "$AWK_PROG" "$1" >> "$out_dir/$file"
  done
}

# Only run the main function if this script was executed directly
#
# If this script is sourced by another script or an interactive session,
#   the function will not run until called directly
#
# If this script is loaded over the network,
#   it will not execute until it reaches the last line
# It won't execute if it fails halfway through, for example.
#
[[ "$0" == "${BASH_SOURCE[0]}" ]] && process_all_files "$@"

関連情報