2

我需要清理一个包含 PHP 序列化值的 mysql 转储(我使用 sed 搜索/替换 url)

在 PHP 中,我会这样做:

<?php

$ret_string = preg_replace('!s:(\d+):"(.*?)";!e', "'s:'.mb_strlen('$2').':\"$2\";'", $string );

?>

你会如何在 bash 中做到这一点?

示例原始字符串:

a:3:{s:7:"string1";s:4:"test";s:3:"url";s:17:"http://myurl.com";s:7:"string2";s:‌​4:"test";} 

我已经用 mynewurl.com 替换了 myurl.com,所以它现在看起来像:

a:3:{s:7:"string1";s:4:"test";s:3:"url";s:17:"http://mynewurl.com";s:7:"string2";s:‌​4:"test";}

我需要更改的是字符串的长度以反映新字符串的长度(s:17),以便最终字符串变为:

a:3:{s:7:"string1";s:4:"test";s:3:"url";s:19:"http://mynewurl.com";s:7:"string2"‌​;s:4:"test";}

我的 sql 转储中有很多这些,它是文件系统上的一个文件。虽然域已经改变,但url可能有一个额外的路径,因此不同的字符串长度

4

2 回答 2

1

在正则表达式中执行此操作的问题在于,正则表达式不适合 JSON 或 PHP 序列化字符串等结构化文本。如果您知道输入数据将始终遵循某种结构,则可以“伪造”事物,但是随着时间的推移,这样编写的代码会出现问题。事情总是会随着时间而改变。如果您可以避免这种黑客攻击,最好这样做。

我们究竟想在这里解决什么问题?您是否无法在需要进行此更改的主机上运行 PHP?序列化的数据在 PHP 中会更容易处理,即使你制作了一个小的 shell 可执行 PHP 脚本来处理它。

[ghoti@pc ~]$ cat indexrepl
#!/usr/bin/env php
<?php

// Usage: indexrepl index newcontent [string]

if ($argc < 4) {
  $s='a:3:{s:7:"string1";s:4:"test";s:3:"url";s:16:"http://myurl.com";s:7:"string2";s:4:"test";}';
} else {
  $s=$argv[3];
}

$a=unserialize($s);
$a[$argv[1]]=$argv[2];

print serialize($a) . "\n";

[ghoti@pc ~]$ 
[ghoti@pc ~]$ 
[ghoti@pc ~]$ ./indexrepl url http://example.com/
a:3:{s:7:"string1";s:4:"test";s:3:"url";s:19:"http://example.com/";s:7:"string2";s:4:"test";}
[ghoti@pc ~]$ 
[ghoti@pc ~]$ s='a:3:{s:7:"string1";s:4:"test";s:3:"url";s:19:"http://example.com/";s:7:"string2";s:4:"test";}'
[ghoti@pc ~]$ ./indexrepl string1 foo "$s"
a:3:{s:7:"string1";s:3:"foo";s:3:"url";s:19:"http://example.com/";s:7:"string2";s:4:"test";}

更新:根据评论将其包装在一个 shell 结构中:

[ghoti@pc ~]$ cat strings.txt
a:1:{s:3:"foo";s:3:"bar";}
a:1:{s:3:"foo";s:3:"baz";}
a:1:{s:3:"foo";s:5:"snert";}
[ghoti@pc ~]$ while read line; do ./indexrepl foo test "$line"; done < strings.txt
a:1:{s:3:"foo";s:4:"test";}
a:1:{s:3:"foo";s:4:"test";}
a:1:{s:3:"foo";s:4:"test";}
[ghoti@pc ~]$ 
于 2012-07-04T03:45:45.210 回答
0

我最终得到了一个 php -r 解决方案。我认为在 bash 中执行此操作会有很多行:

mv "$DATA_DIR/final.sql" "$DATA_DIR/final.sql.1"
php -r  "echo preg_replace('!s:(\\d+):\\\\\\\"(.*?)\\\\\\\";!e', \"'s:'.strlen( str_replace (array('\\\\r', '\\\\n', '\\\\t'), ' ', '\$2') ) .':\\\\\\\"\$2\\\\\\\";'\", file_get_contents('$DATA_DIR/final.sql.1') );" >"$DATA_DIR/final.sql"
rm "$DATA_DIR/final.sql.1";

这适用于我的情况。

于 2012-07-04T23:21:22.900 回答