使用awk或其他替换文件中的整个字段值

时间:2017-07-03 15:44:41

标签: bash postgresql awk monetdb

我从postgresql表导出了多个字段,包括boolean(由postgresql导出为tf字符),我需要将其导入另一个赢得的数据库(monetdb)将t / f理解为bool值。

编辑删除空格以反映真实的文件方面并避免愤怒的评论 - 之前显示的空格)

id|val_str|bool_1|bool2|bool_3|bool4|
1|help|t|t|f|t|
2|test|f|t|f|f|
...

由于我无法替换t / f的所有出现,我需要在我的模式中集成字段分隔符。 我尝试使用awk将字段t替换为TRUE,将f替换为FALSE

awk -F'|' '{gsub(/\|t\|/, "|TRUE|"); gsub(/\|f\|/, "|FALSE|"); print;}' 

这是部分工作,因为具有相同值(|t|t|)的连续字段将仅替换第一个匹配项(|TRUE|t| - 因为第二次出现实际上是t|而不是{ {1}})。

|t|

表有~450列,所以我无法真正指定要替换的列列表,也不能在postgres中使用'转换'布尔列(我可以......)。

我可以运行id|val_str|bool_1|bool2|bool_3|bool4| 1|help|TRUE|t|FALSE|TRUE| 2|test|FALSE|TRUE|FALSE|f| ... 两次,但我正在寻找更优雅的方式来匹配所有字段的整个字段内容。

gsub()没有任何帮助,因为我们大多数时间都处于中间位置。

4 个答案:

答案 0 :(得分:3)

  

表有~450列,所以我无法真正指定列的列表   要被替换,也不能用postgres来'变换'布尔列(I   可以,但......)。

您可以让Postgres为您完成工作。生成SELECT列表的基本查询:

SELECT string_agg(CASE WHEN atttypid = 'bool'::regtype
                       THEN quote_ident(attname) || '::text'
                       ELSE quote_ident(attname) END, ', ' ORDER BY attnum)
FROM   pg_attribute
WHERE  attrelid = 'mytable'::regclass  -- provide table name here
AND    attnum > 0
AND    NOT attisdropped;

生成以下形式的字符串:

col1, "CoL 2", bool1::text, "Bool 2"::text

所有标识符都已正确转义。列是默认顺序。复制并执行它。使用COPY导出到文件。 (或者psql中的\copy。)性能与导出普通表大致相同。如果您不需要大写,请省略upper()

为什么简单的转换为text

关于regclass并正确转义标识符:

如果您需要一个包含TRUE / FALSE / NULL大写的完整语句,标准SQL强制表示法(不带冒号::),仍然是原始列名和也许是一个模式限定的表名:

SELECT 'SELECT '
     || string_agg(CASE WHEN atttypid = 'bool'::regtype
                        THEN format('upper(cast(%1$I AS text)) AS %1$I', attname)
                        ELSE quote_ident(attname) END, ', ' ORDER BY attnum)
     || ' FROM myschema.mytable;'           -- provide table name twice now
FROM   pg_attribute
WHERE  attrelid = 'myschema.mytable'::regclass
AND    attnum > 0
AND    NOT attisdropped;

生成表格的完整声明:

SELECT col1, "CoL 2", upper(cast(bool1 AS text) AS bool1, upper(cast("Bool 2" AS text)) AS "Bool 2" FROM myschema.mytable;

答案 1 :(得分:1)

如果perl没问题,可以使用lookarounds:

$ cat ip.txt 
id |  val_str  | bool_1 | bool2  | bool_3 | bool4  | 
1  |    help   |   t    |   t    |   f    |   t    |
2  |    test   |   f    |   t    |   f    |   f    | 

$ perl -pe 's/\|\K\h*t\h*(?=\|)/  TRUE  /g; s/\|\K\h*f\h*(?=\|)/  FALSE /g' ip.txt 
id |  val_str  | bool_1 | bool2  | bool_3 | bool4  | 
1  |    help   |  TRUE  |  TRUE  |  FALSE |  TRUE  |
2  |    test   |  FALSE |  TRUE  |  FALSE |  FALSE | 
  • \|\K正面看后来匹配|
  • \h*可选的水平空格,如果输入中实际不存在则删除
  • (?=\|)肯定前瞻以匹配|


也可以使用sed循环。在GNU sed 4.2.2上测试,语法可能会因其他实现而异[/ p>

$ sed ':a s/| *t *|/|  TRUE  |/;ta; :b s/| *f *|/|  FALSE |/;tb' ip.txt 
id |  val_str  | bool_1 | bool2  | bool_3 | bool4  | 
1  |    help   |  TRUE  |  TRUE  |  FALSE |  TRUE  |
2  |    test   |  FALSE |  TRUE  |  FALSE |  FALSE | 
  • :a标签
  • s/| *t *|/| TRUE |/替换命令
  • ta只要替换命令成功
  • 就转移到标签a
  • 同样适用于:b


输入中没有空格

perl -pe 's/\|\Kt(?=\|)/TRUE/g; s/\|\Kf(?=\|)/FALSE/g' ip.txt 
sed ':a s/|t|/|TRUE|/;ta; :b s/|f|/|FALSE|/;tb' ip.txt 
awk 'BEGIN{FS=OFS="|"} {for(i=1;i<=NF;i++){if($i=="t"){$i="TRUE"} if($i=="f"){$i="FALSE"}} print}' ip.txt

答案 2 :(得分:1)

假设(根据您的评论)您的输入文件实际上看起来不像您发布的样本,而是看起来像这样:

$ cat file
id|val_str|bool_1|bool2|bool_3|bool4|
1|help|t|t|f|t|
2|test|f|t|f|f|

然后您只需要:

$ awk '{while(gsub(/\|t\|/,"|TRUE|")); while(gsub(/\|f\|/,"|FALSE|"));}1' file
id|val_str|bool_1|bool2|bool_3|bool4|
1|help|TRUE|TRUE|FALSE|TRUE|
2|test|FALSE|TRUE|FALSE|FALSE|

N替换字符串的一般解决方案是:

$ awk 'BEGIN{m["f"]="FALSE"; m["t"]="TRUE"} {for (k in m) while(gsub("\\|"k"\\|","|"m[k]"|"));} 1' file
id|val_str|bool_1|bool2|bool_3|bool4|
1|help|TRUE|TRUE|FALSE|TRUE|
2|test|FALSE|TRUE|FALSE|FALSE|

答案 3 :(得分:0)

使用sed,这是标准。

sed 's/| *t */| TRUE /g;s/| *f */| FALSE /g'

这告诉sed替换以管道字符,未知数量的空格(可能为零),t和空格后跟未知空格数{{1}的每个子字符串。 }};与| TRUE相同。

如果线路长度混乱,则通过f管道输出。