safe-rm 3.2.0 → 3.3.0
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.
- package/.github/workflows/nodejs.yml +1 -0
- package/README.md +22 -2
- package/bin/rm.sh +20 -1
- package/package.json +1 -1
- package/test/cases.js +74 -0
package/README.md
CHANGED
|
@@ -173,8 +173,7 @@ Targets outside the configured scope will be skipped and reported as unsafe.
|
|
|
173
173
|
For example:
|
|
174
174
|
|
|
175
175
|
```sh
|
|
176
|
-
|
|
177
|
-
safe-rm -rf /
|
|
176
|
+
SAFE_RM_SCOPE="$HOME" safe-rm -rf /
|
|
178
177
|
# rm: target '/' skipped, unsafe directory scope
|
|
179
178
|
```
|
|
180
179
|
|
|
@@ -186,6 +185,27 @@ SAFE_RM_SCOPE=. safe-rm -rf ../
|
|
|
186
185
|
# rm: target '../' skipped, unsafe directory scope
|
|
187
186
|
```
|
|
188
187
|
|
|
188
|
+
### Honor Options After Operands (`rm dir -rf`)
|
|
189
|
+
|
|
190
|
+
GNU `rm` (most Linux distros) accepts options anywhere on the command line, so `rm dir -rf` works like `rm -rf dir`. BSD `rm` (MacOS) does not — it stops parsing options at the first operand and would treat `-rf` as a filename.
|
|
191
|
+
|
|
192
|
+
By default, `safe-rm` mirrors the host's real `rm`:
|
|
193
|
+
|
|
194
|
+
- On Linux: options after operands are parsed as flags (GNU style).
|
|
195
|
+
- On MacOS: options after operands are treated as filenames (BSD style).
|
|
196
|
+
|
|
197
|
+
To override the auto-detection (e.g. on Alpine/BusyBox where `rm` does not permute):
|
|
198
|
+
|
|
199
|
+
```sh
|
|
200
|
+
# Force GNU-style permutation
|
|
201
|
+
export SAFE_RM_OPTIONS_ANYWHERE=yes
|
|
202
|
+
|
|
203
|
+
# Force BSD-style strict ordering
|
|
204
|
+
export SAFE_RM_OPTIONS_ANYWHERE=no
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
`--` always terminates option parsing in either mode.
|
|
208
|
+
|
|
189
209
|
### Protect Files And Directories From Deleting
|
|
190
210
|
|
|
191
211
|
If you want to protect some certain files or directories from deleting by mistake, you could create a `.gitignore` file under the `"~/.safe-rm/"` directory, you could write [.gitignore rules](https://git-scm.com/docs/gitignore) inside the file.
|
package/bin/rm.sh
CHANGED
|
@@ -90,6 +90,25 @@ else
|
|
|
90
90
|
fi
|
|
91
91
|
|
|
92
92
|
|
|
93
|
+
# Whether to honor options after the first operand (`rm dir -rf`).
|
|
94
|
+
# Defaults to auto: enabled on Linux, disabled on MacOS. yes|no to override.
|
|
95
|
+
case ${SAFE_RM_OPTIONS_ANYWHERE:0:1} in
|
|
96
|
+
[yY])
|
|
97
|
+
OPTIONS_ANYWHERE=1
|
|
98
|
+
;;
|
|
99
|
+
[nN])
|
|
100
|
+
OPTIONS_ANYWHERE=
|
|
101
|
+
;;
|
|
102
|
+
*)
|
|
103
|
+
if [[ "$OS_TYPE" == "Linux" ]]; then
|
|
104
|
+
OPTIONS_ANYWHERE=1
|
|
105
|
+
else
|
|
106
|
+
OPTIONS_ANYWHERE=
|
|
107
|
+
fi
|
|
108
|
+
;;
|
|
109
|
+
esac
|
|
110
|
+
|
|
111
|
+
|
|
93
112
|
# The target trash directory to dispose files and directories,
|
|
94
113
|
# defaults to the system trash directory
|
|
95
114
|
SAFE_RM_TRASH=${SAFE_RM_TRASH:="$DEFAULT_TRASH"}
|
|
@@ -270,7 +289,7 @@ while [[ -n $1 ]]; do
|
|
|
270
289
|
# -> args: [], files: ['-']
|
|
271
290
|
*)
|
|
272
291
|
push_file "$1"; debug "$LINENO: file $1"
|
|
273
|
-
ARG_END=1
|
|
292
|
+
[[ -z "$OPTIONS_ANYWHERE" ]] && ARG_END=1
|
|
274
293
|
;;
|
|
275
294
|
esac
|
|
276
295
|
fi
|
package/package.json
CHANGED
package/test/cases.js
CHANGED
|
@@ -975,4 +975,78 @@ fi
|
|
|
975
975
|
t.is(result.code, 0, 'exit code should be 0')
|
|
976
976
|
t.false(await pathExists(filepath), 'file should be removed')
|
|
977
977
|
})
|
|
978
|
+
|
|
979
|
+
// Flag-after-operand parsing: align with the host's real `rm`.
|
|
980
|
+
// GNU rm (Linux) permutes options; BSD rm (MacOS) does not.
|
|
981
|
+
!is_rm(type) && !is_as(type) && !IS_MACOS && test(`Linux: flags after operand parse as options`, async t => {
|
|
982
|
+
const {
|
|
983
|
+
createDir,
|
|
984
|
+
createFile,
|
|
985
|
+
runRm,
|
|
986
|
+
pathExists
|
|
987
|
+
} = t.context
|
|
988
|
+
|
|
989
|
+
const dirpath = await createDir()
|
|
990
|
+
await createFile({under: dirpath})
|
|
991
|
+
|
|
992
|
+
const result = await runRm([dirpath, '-rf'])
|
|
993
|
+
|
|
994
|
+
assertEmptySuccess(t, result)
|
|
995
|
+
t.false(await pathExists(dirpath), 'directory should be removed')
|
|
996
|
+
})
|
|
997
|
+
|
|
998
|
+
!is_rm(type) && !is_as(type) && IS_MACOS && test(`MacOS: flags after operand are treated as filenames`, async t => {
|
|
999
|
+
const {
|
|
1000
|
+
createDir,
|
|
1001
|
+
createFile,
|
|
1002
|
+
runRm,
|
|
1003
|
+
pathExists
|
|
1004
|
+
} = t.context
|
|
1005
|
+
|
|
1006
|
+
const dirpath = await createDir()
|
|
1007
|
+
await createFile({under: dirpath})
|
|
1008
|
+
|
|
1009
|
+
const result = await runRm([dirpath, '-rf'])
|
|
1010
|
+
|
|
1011
|
+
t.is(result.code, 1, 'exit code should be 1')
|
|
1012
|
+
t.true(await pathExists(dirpath), 'directory should remain')
|
|
1013
|
+
})
|
|
1014
|
+
|
|
1015
|
+
!is_rm(type) && !is_as(type) && IS_MACOS && test(`SAFE_RM_OPTIONS_ANYWHERE=yes enables permutation on MacOS`, async t => {
|
|
1016
|
+
const {
|
|
1017
|
+
createDir,
|
|
1018
|
+
createFile,
|
|
1019
|
+
runRm,
|
|
1020
|
+
pathExists
|
|
1021
|
+
} = t.context
|
|
1022
|
+
|
|
1023
|
+
const dirpath = await createDir()
|
|
1024
|
+
await createFile({under: dirpath})
|
|
1025
|
+
|
|
1026
|
+
const result = await runRm([dirpath, '-rf'], {
|
|
1027
|
+
env: {SAFE_RM_OPTIONS_ANYWHERE: 'yes'}
|
|
1028
|
+
})
|
|
1029
|
+
|
|
1030
|
+
assertEmptySuccess(t, result)
|
|
1031
|
+
t.false(await pathExists(dirpath), 'directory should be removed')
|
|
1032
|
+
})
|
|
1033
|
+
|
|
1034
|
+
!is_rm(type) && !is_as(type) && !IS_MACOS && test(`SAFE_RM_OPTIONS_ANYWHERE=no disables permutation on Linux`, async t => {
|
|
1035
|
+
const {
|
|
1036
|
+
createDir,
|
|
1037
|
+
createFile,
|
|
1038
|
+
runRm,
|
|
1039
|
+
pathExists
|
|
1040
|
+
} = t.context
|
|
1041
|
+
|
|
1042
|
+
const dirpath = await createDir()
|
|
1043
|
+
await createFile({under: dirpath})
|
|
1044
|
+
|
|
1045
|
+
const result = await runRm([dirpath, '-rf'], {
|
|
1046
|
+
env: {SAFE_RM_OPTIONS_ANYWHERE: 'no'}
|
|
1047
|
+
})
|
|
1048
|
+
|
|
1049
|
+
t.is(result.code, 1, 'exit code should be 1')
|
|
1050
|
+
t.true(await pathExists(dirpath), 'directory should remain')
|
|
1051
|
+
})
|
|
978
1052
|
}
|