inplace 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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