inplace 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ require File.expand_path('../../inplace', __FILE__)
@@ -0,0 +1,254 @@
1
+ .\" $Idaemons: /home/cvs/inplace/inplace.1,v 1.8 2004/04/21 13:25:51 knu Exp $
2
+ .\" $Id$
3
+ .\"
4
+ .Dd April 7, 2004
5
+ .Dt INPLACE 1
6
+ .Os FreeBSD
7
+ .Sh NAME
8
+ .Nm inplace
9
+ .Nd edits files in-place through given filter commands
10
+ .Sh SYNOPSIS
11
+ .Nm
12
+ .Op Fl DLfinstvz
13
+ .Op Fl b Ar suffix
14
+ .Fl e Ar commandline
15
+ .Op Oo Fl e Ar commandline Oc ...
16
+ .Op Ar file ...
17
+ .Nm
18
+ .Op Fl DLfinstvz
19
+ .Op Fl b Ar suffix
20
+ .Ar commandline
21
+ .Op Ar file ...
22
+ .Sh DESCRIPTION
23
+ The
24
+ .Nm
25
+ command is a utility to edit files in-place through given filter
26
+ commands preserving the original file attributes. Mode and ownership
27
+ (user and group) are preserved by default, and time (access and
28
+ modification) by choice.
29
+ .Pp
30
+ Inode numbers will change by default, but there is a
31
+ .Fl i
32
+ option with which given the inode number of each edited file will be
33
+ preserved.
34
+ .Pp
35
+ As for filter commands, a single command may be specified as the first
36
+ argument to
37
+ .Nm .
38
+ To pass many filter commands, specify each followed by the
39
+ .Fl e
40
+ option.
41
+ .Pp
42
+ There are some cases where
43
+ .Nm
44
+ does not replace a file, such as when:
45
+ .Bl -enum -offset indent
46
+ .It
47
+ The original file is not writable (use
48
+ .Fl f
49
+ to force editing against read-only files)
50
+ .It
51
+ A filter command fails and exits with a non-zero return code
52
+ .It
53
+ The resulted output is identical to the original file
54
+ .It
55
+ The resulted output is empty (use
56
+ .Fl z
57
+ to accept empty output)
58
+ .El
59
+ .Pp
60
+ .Sh OPTIONS
61
+ The following command line arguments are supported:
62
+ .Pp
63
+ .Bl -tag -width "--preserve-timestamp" -compact
64
+ .It Fl h
65
+ .It Fl -help
66
+ Show help and exit.
67
+ .Pp
68
+ .It Fl D
69
+ .It Fl -debug
70
+ Turn on debug output.
71
+ .Pp
72
+ .It Fl L
73
+ .It Fl -dereference
74
+ By default,
75
+ .Nm
76
+ ignores non-regular files including symlinks, but this switch makes it
77
+ resolve (dereference) each symlink using realpath(3) and edit the
78
+ original file.
79
+ .Pp
80
+ .It Fl b Ar SUFFIX
81
+ .It Fl -backup-suffix Ar SUFFIX
82
+ Create a backup file with the given suffix for each file. Note that
83
+ backup files will be written over existing files, if any.
84
+ .Pp
85
+ .It Fl e Ar COMMANDLINE
86
+ .It Fl -execute Ar COMMANDLINE
87
+ Specify a filter command line to run for each file in which the following placeholders can be used:
88
+ .Bl -tag -offset indent -nested
89
+ .It Cm %0
90
+ replaced by the original file path, shell escaped with
91
+ .Pf \e 's
92
+ as necessary
93
+ .It Cm %1
94
+ replaced by the source file path, shell escaped with
95
+ .Pf \e 's
96
+ as necessary
97
+ .It Cm %2
98
+ replaced by the destination file path, shell escaped with
99
+ .Pf \e 's
100
+ as necessary
101
+ .It Cm %%
102
+ replaced by
103
+ .Ql %
104
+ .El
105
+ .Pp
106
+ Omission of %2 indicates %1 should be modified destructively, and
107
+ omission of both %1 and %2 implies
108
+ .Dq Li "(...) < %1 > %2"
109
+ around the command line.
110
+ .Pp
111
+ When the filter command is run, the destination file is always an
112
+ empty temporary file, and the source file is either the original file
113
+ or a temporary copy file.
114
+ .Pp
115
+ Every temporary file has the same suffix as the original file, so that
116
+ file name aware programs can play nicely with it.
117
+ .Pp
118
+ Instead of specifying a whole command line, you can use a command
119
+ alias defined in a configuration file,
120
+ .Pa ~/.inplace .
121
+ See the FILES section for the file format.
122
+ .Pp
123
+ This option can be specified many times, and they will be executed in
124
+ sequence. A file is only replaced if all of them succeeds.
125
+ .Pp
126
+ See the EXAMPLES section below for details.
127
+ .Pp
128
+ .It Fl f
129
+ .It Fl -force
130
+ By default,
131
+ .Nm
132
+ does not perform editing if a file is not writable. This switch makes
133
+ it force editing even if a file to process is read-only.
134
+ .Pp
135
+ .It Fl i
136
+ .It Fl -preserve-inode
137
+ Make sure to preserve the inode number of each file.
138
+ .Pp
139
+ .It Fl n
140
+ .It Fl -dry-run
141
+ Do not perform any destructive operation and just show what would have
142
+ been done. This switch implies
143
+ .Fl v .
144
+ .Pp
145
+ .It Fl s
146
+ .It Fl -same-directory
147
+ Create a temporary file in the same directory as each replaced file.
148
+ This may speed up the performance when the directory in question is on
149
+ a partition that is fast enough and the system temporary directory is
150
+ slow.
151
+ .Pp
152
+ This switch can be effectively used when the temporary directory does
153
+ not have sufficient disk space for a resulted file.
154
+ .Pp
155
+ If this option is specified, edited files will have newly assigned
156
+ inode numbers. To prevent this, use the
157
+ .Fl i
158
+ option.
159
+ .Pp
160
+ .It Fl t
161
+ .It Fl -preserve-timestamp
162
+ Preserve the access and modification times of each file.
163
+ .Pp
164
+ .It Fl v
165
+ .It Fl -verbose
166
+ Turn on verbose mode.
167
+ .Pp
168
+ .It Fl z
169
+ .It Fl -accept-empty
170
+ By default,
171
+ .Nm
172
+ does not replace the original file when a resulted file is empty in
173
+ size because it is likely that there is a mistake in the filter
174
+ command. This switch makes it accept empty (zero-sized) output and
175
+ replace the original file with it.
176
+ .El
177
+ .Sh EXAMPLES
178
+ .Bl -bullet
179
+ .It
180
+ Sort files in-place using
181
+ .Xr sort 1 :
182
+ .Pp
183
+ .Dl inplace sort file1 file2 file3
184
+ .Pp
185
+ Below works the same as above, passing each input file via the command
186
+ line argument:
187
+ .Pp
188
+ .Dl inplace 'sort %1 > %2' file1 file2 file3
189
+ .Pp
190
+ .It
191
+ Perform in-place charset conversion and newline code conversion:
192
+ .Pp
193
+ .Dl inplace -e 'iconv -f EUC-JP -t UTF-8' -e 'perl -pe \&"s/$/\e\er/\&"' file1 file2 file3
194
+ .Pp
195
+ .It
196
+ Process image files taking backup files:
197
+ .Pp
198
+ .Dl inplace -b.orig 'convert -rotate 270 -resize 50%% %1 %2' *.jpg
199
+ .Pp
200
+ .It
201
+ Perform a mass MP3 tag modification without changing timestamps:
202
+ .Pp
203
+ .Dl find mp3/Some_Artist -name '*.mp3' -print0 | xargs -0 inplace -te 'mp3info -a \&"Some Artist\&" -g \&"Progressive Rock\&" %1'
204
+ .Pp
205
+ As you see above,
206
+ .Nm
207
+ makes a nice combo with
208
+ .Xr find 1
209
+ and
210
+ .Xr xargs 1 .
211
+ .Pp
212
+ .El
213
+ .Sh FILES
214
+ .Bl -tag -width "~/.inplace"
215
+ .It Pa ~/.inplace
216
+ The configuration file, which syntax is described as follows:
217
+ .Bl -bullet
218
+ .It
219
+ Each alias definition is a name/value pair separated with an
220
+ .Dq = ,
221
+ one per line.
222
+ .It
223
+ White spaces at the beginning or the end of a line, and around
224
+ assignment separators
225
+ .Pf ( Dq = )
226
+ are stripped off.
227
+ .It
228
+ Lines starting with a
229
+ .Dq #
230
+ are ignored.
231
+ .El
232
+ .El
233
+ .Sh ENVIRONMENT
234
+ .Bl -tag -width "TMPDIR" -compact
235
+ .It Ev TMPDIR
236
+ .It Ev TMP
237
+ .It Ev TEMP
238
+ Temporary directory candidates where
239
+ .Nm
240
+ attempts to create intermediate output files, in that order. If none
241
+ is available and writable,
242
+ .Pa /tmp
243
+ is used. If
244
+ .Fl s
245
+ is specified, they will not be used.
246
+ .El
247
+ .Sh SEE ALSO
248
+ .Xr find 1 ,
249
+ .Xr xargs 1 ,
250
+ .Xr realpath 3
251
+ .Sh AUTHORS
252
+ .An Akinori MUSHA Aq knu@iDaemons.org
253
+ .Sh BUGS
254
+ There may always be some bugs. Use at your own risk.
@@ -0,0 +1,292 @@
1
+ #!/bin/sh
2
+ #
3
+ # $Id$
4
+
5
+ flunk () {
6
+ echo "$0: $@" >&2
7
+ exit 1
8
+ }
9
+
10
+ srcdir="$(cd "$(dirname "$0")"/.. && pwd)" || exit 1
11
+ testdir=$srcdir/test/t
12
+ ruby=${RUBY:-$(which ruby)}
13
+
14
+ while getopts d opt; do
15
+ case "$opt" in
16
+ d)
17
+ debug=1
18
+ ;;
19
+ esac
20
+ done
21
+
22
+ shift $(($OPTIND - 1))
23
+
24
+ initialize () {
25
+ mkdir -p $testdir || flunk "mkdir failed"
26
+ cd $testdir || flunk "cd failed"
27
+ [ $(ls | wc -l) -eq 0 ] || rm -i *
28
+ }
29
+
30
+ setup () {
31
+ (echo aaa; echo bbb; echo ccc) > abc.txt
32
+ (echo AAA; echo BBB; echo CCC) > _ABC_.txt
33
+ (echo aaa; echo bbb; echo bbb) > abb.txt
34
+ (echo AAA; echo BBB; echo BBB) > _ABB_.txt
35
+ (echo ccc; echo bbb; echo aaa) > cba.txt
36
+ (echo CCC; echo BBB; echo AAA) > _CBA_.txt
37
+ (echo a b c) > "a b c.txt"
38
+ (echo c b a) > "c b a.txt"
39
+
40
+ ln -s _ABC_.txt _ABC_l.txt
41
+ ln -s _ABC_l.txt _ABC_ll.txt
42
+
43
+ for f in *.txt; do
44
+ cp -p "$f" "$f.orig"
45
+ done
46
+
47
+ sleep 1
48
+ }
49
+
50
+ teardown () {
51
+ rm *.txt*
52
+ }
53
+
54
+ debug () {
55
+ if [ "$debug" = 1 ]; then
56
+ echo "debug: $@" >&2
57
+ fi
58
+ }
59
+
60
+ terminate () {
61
+ cd $srcdir
62
+ rm -rf $testdir
63
+ }
64
+
65
+ inplace_flags=
66
+
67
+ inplace () {
68
+ local file i1 i2 has_i
69
+ has_i=0
70
+
71
+ file="$($ruby -e 'puts ARGV.last' -- "$@")"
72
+ i1="$(inode_of "$file")"
73
+ $srcdir/lib/inplace.rb $inplace_flags "$@" >/dev/null 2>&1
74
+ i2="$(inode_of "$file")"
75
+
76
+ if echo " $inplace_flags" | fgrep -qe " -i"; then
77
+ has_i=1
78
+ fi
79
+
80
+ if [ $has_i = 1 -a "$i1" != "$i2" ]; then
81
+ echo "inode changed!" >&2
82
+ fi
83
+ }
84
+
85
+ inode_of () {
86
+ expr "$(ls -i "$@")" : '\([0-9]*\)'
87
+ }
88
+
89
+ cmp_file () {
90
+ if cmp -s "$1" "$2"; then
91
+ debug "$1 == $2"
92
+ return 0
93
+ else
94
+ debug "$1 != $2"
95
+ return 1
96
+ fi
97
+ }
98
+
99
+ cmp_time () {
100
+ # Since Ruby's File.stat() does not obtain nanosec for the moment,
101
+ # inplace(1) cannot preserve nanosec values and test(1)'s strict
102
+ # nanosec-wise check does not pass..
103
+ #test ! "$1" -nt "$2" -a ! "$2" -nt "$1"
104
+
105
+ if $ruby -e 'File.mtime(ARGV[0]) == File.mtime(ARGV[1]) or exit 1' "$1" "$2"; then
106
+ debug "mtime($1) == mtime($2)"
107
+ return 0
108
+ else
109
+ debug "mtime($1) != mtime($2)"
110
+ return 1
111
+ fi
112
+ }
113
+
114
+ test1 () {
115
+ # simple feature test 1 - no argument
116
+ inplace 'sort -r' _CBA_.txt
117
+ test -e _CBA_.txt.bak && return 1
118
+ cmp_file _CBA_.txt _CBA_.txt.orig || return 1
119
+ cmp_time _CBA_.txt _CBA_.txt.orig || return 1
120
+
121
+ inplace 'sort -r' abc.txt
122
+ cmp_file abc.txt cba.txt.orig || return 1
123
+ cmp_time abc.txt abc.txt.orig && return 1
124
+
125
+ inplace 'tr a-z A-Z' abb.txt
126
+ cmp_file abb.txt _ABB_.txt.orig || return 1
127
+ cmp_time abb.txt abb.txt.orig && return 1
128
+
129
+ inplace 'rev' 'a b c.txt'
130
+ cmp_file 'a b c.txt' 'c b a.txt.orig' || return 1
131
+ cmp_time 'a b c.txt' 'a b c.txt.orig' && return 1
132
+
133
+ inplace -e 'sort -r' -e 'tr A-Z a-z' _ABC_.txt
134
+ cmp_file _ABC_.txt cba.txt.orig || return 1
135
+ cmp_time _ABC_.txt cba.txt.orig && return 1
136
+
137
+ inplace -t 'sort' cba.txt
138
+ cmp_file cba.txt abc.txt.orig || return 1
139
+ cmp_time cba.txt cba.txt.orig || return 1
140
+
141
+ return 0
142
+ }
143
+
144
+ test2 () {
145
+ # simple feature test 2 - 2 arguments
146
+ inplace 'sort -r %1 > %2' _CBA_.txt
147
+ test -e _CBA_.txt.bak && return 1
148
+ cmp_file _CBA_.txt _CBA_.txt.orig || return 1
149
+ cmp_time _CBA_.txt _CBA_.txt.orig || return 1
150
+
151
+ inplace 'sort -r %1 > %2' abc.txt
152
+ cmp_file abc.txt cba.txt.orig || return 1
153
+ cmp_time abc.txt abc.txt.orig && return 1
154
+
155
+ inplace 'tr a-z A-Z < %1 > %2' abb.txt
156
+ cmp_file abb.txt _ABB_.txt.orig || return 1
157
+ cmp_time abb.txt abb.txt.orig && return 1
158
+
159
+ inplace 'rev %1 > %2' 'a b c.txt'
160
+ cmp_file 'a b c.txt' 'c b a.txt.orig' || return 1
161
+ cmp_time 'a b c.txt' 'a b c.txt.orig' && return 1
162
+
163
+ inplace -e 'sort -r %1 > %2' -e 'tr A-Z a-z' _ABC_.txt
164
+ cmp_file _ABC_.txt cba.txt.orig || return 1
165
+ cmp_time _ABC_.txt cba.txt.orig && return 1
166
+
167
+ inplace -t 'sort %1 > %2' cba.txt
168
+ cmp_file cba.txt abc.txt.orig || return 1
169
+ cmp_time cba.txt cba.txt.orig || return 1
170
+
171
+ return 0
172
+ }
173
+
174
+ test3 () {
175
+ # simple feature test 3 - 1 argument
176
+ inplace "$ruby -i -pe '\$_.upcase!' %1" _CBA_.txt
177
+ test -e _CBA_.txt.bak && return 1
178
+ cmp_file _CBA_.txt _CBA_.txt.orig || return 1
179
+ cmp_time _CBA_.txt _CBA_.txt.orig || return 1
180
+
181
+ inplace "$ruby -i -pe '\$_.upcase!' %1" abb.txt
182
+ cmp_file abb.txt _ABB_.txt.orig || return 1
183
+ cmp_time abb.txt abb.txt.orig && return 1
184
+
185
+ inplace "$ruby -i -pe '\$_.chomp!; \$_ = \$_.reverse + \"\\n\"' %1" 'a b c.txt'
186
+ cmp_file 'a b c.txt' 'c b a.txt' || return 1
187
+ cmp_time 'a b c.txt' 'a b c.txt.orig' && return 1
188
+
189
+ inplace -e "$ruby -i -pe '\$_.tr!(\"a\", \"A\")' %1" -e "$ruby -i -pe '\$_.tr!(\"bc\", \"BC\")' %1" abc.txt
190
+ cmp_file abc.txt _ABC_.txt.orig || return 1
191
+ cmp_time abc.txt _ABC_.txt.orig && return 1
192
+
193
+ return 0
194
+ }
195
+
196
+ test4 () {
197
+ # backup test
198
+ inplace -b.bak 'sort -r' abc.txt
199
+ cmp_file abc.txt cba.txt.orig || return 1
200
+ cmp_file abc.txt.bak abc.txt.orig || return 1
201
+ cmp_time abc.txt abc.txt.orig && return 1
202
+ cmp_time abc.txt.bak abc.txt.orig || return 1
203
+
204
+ inplace -b.bak -t 'sort' cba.txt
205
+ cmp_file cba.txt abc.txt.orig || return 1
206
+ cmp_file cba.txt.bak cba.txt.orig || return 1
207
+ cmp_time cba.txt cba.txt.orig || return 1
208
+ cmp_time cba.txt.bak cba.txt.orig || return 1
209
+
210
+ return 0
211
+ }
212
+
213
+ test5 () {
214
+ # error test
215
+ inplace -b.bak 'sort -r; exit 1' abc.txt
216
+ test -e abc.txt.bak && return 1
217
+ cmp_file abc.txt abc.txt.orig || return 1
218
+ cmp_time abc.txt abc.txt.orig || return 1
219
+
220
+ inplace -b.bak -e 'sort' -e 'exit 1' -e 'tr a-z A-Z' cba.txt
221
+ test -e cba.txt.bak && return 1
222
+ cmp_file cba.txt cba.txt.orig || return 1
223
+ cmp_time cba.txt cba.txt.orig || return 1
224
+ }
225
+
226
+ test6 () {
227
+ # zero-sized output test
228
+ inplace -b.bak 'cat /dev/null' abb.txt
229
+ test -e abb.txt.bak && return 1
230
+ cmp_file abb.txt abb.txt.orig || return 1
231
+ cmp_time abb.txt abb.txt.orig || return 1
232
+
233
+ inplace -z -b.bak 'cat /dev/null' abb.txt
234
+ test -s abb.txt && return 1
235
+ cmp_file abb.txt.bak abb.txt.orig || return 1
236
+ cmp_time abb.txt.bak abb.txt.orig || return 1
237
+
238
+ return 0
239
+ }
240
+
241
+ test7 () {
242
+ # symlink test
243
+ inplace -b.bak 'sort -r' _ABC_ll.txt
244
+ test -e _ABC_ll.txt.bak && return 1
245
+ test -e _ABC_l.txt.bak && return 1
246
+ test -e _ABC_.txt.bak && return 1
247
+ cmp_file _ABC_.txt _ABC_.txt.orig || return 1
248
+ cmp_time _ABC_.txt _ABC_.txt.orig || return 1
249
+
250
+ inplace -L -b.bak 'sort -r' _ABC_ll.txt
251
+ test -e _ABC_ll.txt.bak && return 1
252
+ test -e _ABC_l.txt.bak && return 1
253
+ test -e _ABC_.txt.bak || return 1
254
+ cmp_file _ABC_.txt _CBA_.txt.orig || return 1
255
+ cmp_file _ABC_.txt.bak _ABC_.txt.orig || return 1
256
+
257
+ return 0
258
+ }
259
+
260
+ main () {
261
+ initialize
262
+
263
+ n=7
264
+ error=0
265
+
266
+ for inplace_flags in '' '-s' '-i' '-i -s'; do
267
+ for i in $(jot $n 1 $n); do
268
+ if [ X"$inplace_flags" != X"" ]; then
269
+ printf "%s with %s..." "test$i" "$inplace_flags"
270
+ else
271
+ printf "%s..." "test$i"
272
+ fi
273
+
274
+ setup
275
+
276
+ if eval test$i; then
277
+ echo "ok"
278
+ else
279
+ echo "failed!"
280
+ error=1
281
+ fi
282
+
283
+ teardown
284
+ done
285
+ done
286
+
287
+ terminate
288
+
289
+ return $error
290
+ }
291
+
292
+ main