我在尝试着:
- 始终添加
0 0 0
到文件的第一行。 - 将
2*pi
或6.2832
乘以三列文件的第一列,其格式类似于下面,但前提是该行以数字开头。第二列和第三列保持原样。 *
如果行不以数字开头,则在行的开头附加 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 脚本。它建立在埃德莫顿的精彩答案之上,我无法真正改进它
好处:
TAU
提取到变量的幻数 ( )- 错误处理 (
TODO
) - 使用输入参数和可选输出参数更容易运行
- 如果文件很多,则报告进度
- 避免间距问题(参见
NOTE
s) - 交互式和独立执行
缺点:
bash
不是sh
- 这么多台词(即使没有注释)
#!/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 "$@"