こんにちは、次のループを実行して複数の緯度と経度にremapnnコマンドを適用できます。
era_Temperature_2016era_Temperature_2017era_Temperature_2018era_Temperature_2019era_Temperature_2020という名前のNETCDFファイルがたくさんあり、これらすべてのファイルにループを適用したいと思います。
#!/bin/bash
infile="era_temperature_2016.nc"
coords="coords.txt"
while read line
do
line=$(echo $line | sed -e 's/\r//g')
station=$(echo $line | cut -d ' ' -f 1)
#-- skip header line
if [[ "$station" == "station" ]]; then continue; fi
#-- select station coordinates
lat=$(echo $line | cut -d ' ' -f 2)
lon=$(echo $line | cut -d ' ' -f 3)
station="${station}_${lat}_${lon}"
#-- extract the station data
cdo -remapnn,"lon=${lon}_lat=${lat}" ${infile} ${station}_out.nc
done < $coords
以下を試しましたが、エラーが発生します。
間違い ./values1.sh:行5:coords="coords.txt"' ./values1.sh: line 5:
予期しないタグcoords = "coords.txt"'の近くに構文エラーがあります。
#!/bin/bash
my_files=$(ls era_temperature_*.nc)
for f in $my_files
coords="coords.txt"
while read line
do
line=$(echo $line | sed -e 's/\r//g')
station=$(echo $line | cut -d ' ' -f 1)
#-- skip header line
if [[ "$station" == "station" ]]; then continue; fi
#-- select station coordinates
lat=$(echo $line | cut -d ' ' -f 2)
lon=$(echo $line | cut -d ' ' -f 3)
station="${station}_${lat}_${lon}"
#-- extract the station data
cdo -remapnn,"lon=${lon}_lat=${lat}" ${infile} ${station}_out.nc
done < $coords
皆様のご意見・ご協力ありがとうございます
以下のコードはうまくいきます
#!/bin/bash
for NUM in $(seq 2016 2018)
do
infile=era_temperature_$NUM.nc
coords="coords.txt"
while read line
do
line=$(echo $line | sed -e 's/\r//g')
station=$(echo $line | cut -d ' ' -f 1)
#-- skip header line
if [[ "$station" == "station" ]]; then continue; fi
#-- select station coordinates
lat=$(echo $line | cut -d ' ' -f 2)
lon=$(echo $line | cut -d ' ' -f 3)
station="${station}_${NUM}_${lat}_${lon}"
#-- extract the station data
cdo -remapnn,"lon=${lon}_lat=${lat}" ${infile} ${station}_out.nc
done < $coords
done
答え1
Bashfor
ループで、次の構文に従ってください。
for <variable name> in <a list of items> ; do <some command> ; done
それを分析しましょう。
for
配列を繰り返すことをシェルに通知します。
<variable name>
現在反復している配列の項目を保存する場所をシェルに提供します。
in <a list of items>
繰り返す配列を指定します。
;
スクリプト内のセミコロンまたは実際の改行文字である可能性がある改行文字を指定します。
do <some command>
はループで実行したいコマンドで、以前にforループで定義されていた変数を含めることができますが、必ずしもそうではありません。
;
今回はループ終了を準備するために改行します。
done
これでループが閉じます。
したがって、for f in $my_files
追加した内容から、後に改行文字があることがわかりますが、シェルがdo
期待したaを定義するのではなく、シェルが予期しない変数を定義しました。シェルは、これが起こるとは思わないため、構文エラーメッセージで終了します。done
ループしたいコードの終わりにも終端はありません。ループwhile
には終端がありますが、done
ループには終端はありませんfor
。
また、次のことを検討してください。lsの解析を避ける。問題が発生する可能性があります。ファイルの繰り返しなどの簡単な操作の場合は、以下を削除すると同じ操作を簡単に実行できますls
。
thegs@wk-thegs-01:test$ ls
test1.txt test2.txt test3.txt
thegs@wk-thegs-01:test$ for file in test*.txt ; do echo $file ; done
test1.txt
test2.txt
test3.txt
続行する前にループ構文を調べることも悪くありません。 Redhatは以下を提供します。アクセシビリティ文書Bashのループについては、読書を強くお勧めします(残念ながら構文解析しますls
が、完璧な人はいません)。
答え2
Shell はデータ操作に誤った言語です。awk
、またはperl
(python
またはシェル以外のほとんどすべての言語)を使用する必要があります。バラよりシェルループを使用してテキストを処理するのはなぜ悪い習慣と見なされますか?そしてスペースやその他の特殊文字が原因でシェルスクリプトが停止するのはなぜですか?多くの理由があります。
さらに、多くの言語にはNetCDFデータを操作するためのライブラリモジュールがあります。たとえば、PerlPDL::NetCDFPythonでネットワーク CDF4。
NetCDF処理ライブラリを使用せずにシェルで実行できる一般的なタスクをスクリプト化する方が簡単ですawk
。perl
たとえば、これはPerlバージョンのスクリプトです。 Perlを選択した理由は、sed、awk、cut、trの多くの機能を1つの言語に組み合わせて非常に便利なためですsplit()
。そして最後に、Perlのsystem()
関数は引数の代わりに引数のセットを使うことができるからです。単純な文字列ではありません(シェルと同じ迷惑を引き起こし、同じ解決策が必要です)。
#!/usr/bin/perl
use strict;
my @coords=();
# Read coords.txt into an array, so we don't have to read it
# again for each year.
#
# Yes, you could read coords.txt into an array in bash too - I very
# strongly encourage you to do so if you decide to stick to shell.
# In bash, its probably best to read coords.txt into three arrays, one
# each for station, lon, and lat. Or two associative arrays, one each
# for lon and lat (both with station as the key).
# Anyway, see `help mapfile` in bash.
my $coords = "coords.txt";
open(my $C, "<", $coords) || die "couldn't open $coords for read: $!\n";
while(<$C>) {
next if /^station/; # skip header
chomp; # get rid of \n, \r, or \r\n line-endings
push @coords, $_;
};
close($C);
# process each year
foreach my $num (2016..2018) {
my $infile = "era_temperature_$num.nc";
# process the coords data for the current year
foreach (@coords) {
my ($station, $lat, $lon) = split;
$outfile = "${station}_${num}_${lat}_${lon}_out.nc";
system("cdo", "-remapnn", "lon=${lon}_lat=${lat}", $infile, $outfile);
};
};
各変数全体を次のように渡すので、引用符なしでsystem()
使用するのは完全に安全です。$infile
$outfile
一つ主張がcdo
何であれ。これはいいえbash は true -$infile
または$outfile
スペースまたはシェルのメタ文字 (たとえば、;
)&
が含まれ、二重引用符なしで使用される場合、シェルの単語の分離と解釈の影響を受けます。〜するスクリプトは中断されます(したがって、シェルでは常に二重引用符で変数を引用する必要があります)。
これは、2つの連想配列を使用する代替バージョンです。これはsplit()
coords.txt の各行に対して一度だけ使用すればよいので、少し早くすることができますが、coords.txt ファイルに何千もの行がないと目立たないでしょう。
#!/usr/bin/perl
use strict;
my %lon = ();
my %lat = ();
# Read coords.txt into two hashes (associative arrays), one
# each for lon and lat.
my $coords = "coords.txt";
open(my $C, "<", $coords) || die "couldn't open $coords for read: $!\n";
while(<$C>) {
next if /^station/; # skip header
chomp; # get rid of \n, \r, or \r\n
my ($station, $lat, $lon) = split;
$lat{$station} = $lat;
$lon{$station} = $lon;
}
close($C);
foreach my $num (2016..2018) {
my $infile = "era_temperature_$num.nc";
foreach my $station (sort keys %lat) {
# Two different ways of constructing a string from other variables.
# Simple interpolation, as in the first version above:
my $outfile = "${station}_${num}_${lat{$station}}_${lon{$station}}";
# And string concatenation with `.`, which can be easier to read
# in some cases.
my $lonlat = "lon=" . $lon{$station} . "_lat=" . $lat{$station};
# Another method is to use sprintf, which can be even easier to read.
# For example, use the following instead of the line above:
# my $lonlat = sprintf "lon=%s_lat=%s", $lon{$station}, $lat{$station};
#
# note: bash has a printf built-in too. I highly recommend using it.
system("cdo", "-remapnn", $lonlat, $infile, $outfile);
};
};
しかし、Perlには非常に便利な参照演算子もあります。たとえば、次の行を次のように書くことがqw()
できます。system()
system(qw(cdo -remapnn lon=${lon}_lat=${lat} $infile $outfile));
または(連想配列バージョンの場合):
system(qw(cdo -remapnn $lonlat $infile $outfile));
perldoc -f qw
詳細より。
最後に、一部の人々はPerlが読んだり理解したりするのが難しいと無意識のうちに主張しています。 (AFAICTこれは主にPerlにsedのような正規表現演算子があることを恐れているためです。あるシェルスクリプトよりもクリーンで読みやすく理解しやすいです。また、4回sed
分岐する必要がないため、より速く実行されます。cut
各ループの繰り返し(例:coords.txtの行数に関係なく3回)。