使用命令行,将列中的所有条目乘以固定数字,但前提是该行不是注释

使用命令行,将列中的所有条目乘以固定数字,但前提是该行不是注释

我在尝试着:

  1. 始终添加0 0 0到文件的第一行。
  2. 2*pi6.2832乘以三列文件的第一列,其格式类似于下面,但前提是该行以数字开头。第二列和第三列保持原样。
  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

附言。 Linux 盒子是离网的并且没有mlr.我也没有 root/admin 凭据。

非常感谢社区的任何帮助。提前致谢,

C

答案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字母表;但将a标志添加到s{...}{...}ae替换中,以便\d仅匹配 ASCII 十进制数字,因为 perl 只能使用 ASCII 十进制数字进行计算,而不是其他脚本的计算)。

如果没有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.832000实际的周长62.831853

答案4

您似乎正在尝试处理比标题所暗示的更大的任务。

这是一个完整的 bash 脚本。它建立在埃德莫顿的精彩答案之上,我无法真正改进它

好处:

  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 "$@"

相关内容