@fredlackey/devutils 0.0.1 → 0.0.3
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/README.md +5 -5
- package/package.json +1 -1
- package/src/commands/install.js +374 -36
- package/src/installs/adobe-creative-cloud.js +527 -25
- package/src/installs/adobe-creative-cloud.md +605 -0
- package/src/installs/appcleaner.js +303 -26
- package/src/installs/appcleaner.md +699 -0
- package/src/installs/apt-transport-https.js +390 -0
- package/src/installs/apt-transport-https.md +678 -0
- package/src/installs/atomicparsley.js +624 -26
- package/src/installs/atomicparsley.md +795 -0
- package/src/installs/aws-cli.js +779 -26
- package/src/installs/aws-cli.md +727 -0
- package/src/installs/balena-etcher.js +688 -26
- package/src/installs/balena-etcher.md +761 -0
- package/src/installs/bambu-studio.js +912 -26
- package/src/installs/bambu-studio.md +780 -0
- package/src/installs/bash-completion.js +554 -23
- package/src/installs/bash-completion.md +833 -0
- package/src/installs/bash.js +399 -26
- package/src/installs/bash.md +993 -0
- package/src/installs/beyond-compare.js +585 -26
- package/src/installs/beyond-compare.md +813 -0
- package/src/installs/build-essential.js +511 -26
- package/src/installs/build-essential.md +977 -0
- package/src/installs/ca-certificates.js +618 -0
- package/src/installs/ca-certificates.md +937 -0
- package/src/installs/caffeine.js +490 -26
- package/src/installs/caffeine.md +839 -0
- package/src/installs/camtasia.js +577 -25
- package/src/installs/camtasia.md +762 -0
- package/src/installs/chatgpt.js +458 -26
- package/src/installs/chatgpt.md +814 -0
- package/src/installs/chocolatey.js +447 -0
- package/src/installs/chocolatey.md +661 -0
- package/src/installs/chrome-canary.js +472 -26
- package/src/installs/chrome-canary.md +641 -0
- package/src/installs/chromium.js +645 -26
- package/src/installs/chromium.md +838 -0
- package/src/installs/claude-code.js +558 -26
- package/src/installs/claude-code.md +1173 -0
- package/src/installs/curl.js +361 -26
- package/src/installs/curl.md +714 -0
- package/src/installs/cursor.js +561 -26
- package/src/installs/cursor.md +970 -0
- package/src/installs/dbschema.js +674 -26
- package/src/installs/dbschema.md +925 -0
- package/src/installs/dependencies.md +435 -0
- package/src/installs/development-tools.js +600 -0
- package/src/installs/development-tools.md +977 -0
- package/src/installs/docker.js +1010 -25
- package/src/installs/docker.md +1109 -0
- package/src/installs/drawio.js +1001 -26
- package/src/installs/drawio.md +795 -0
- package/src/installs/elmedia-player.js +328 -25
- package/src/installs/elmedia-player.md +556 -0
- package/src/installs/ffmpeg.js +870 -25
- package/src/installs/ffmpeg.md +852 -0
- package/src/installs/file.js +464 -0
- package/src/installs/file.md +987 -0
- package/src/installs/gemini-cli.js +793 -26
- package/src/installs/gemini-cli.md +1153 -0
- package/src/installs/git.js +382 -26
- package/src/installs/git.md +907 -0
- package/src/installs/gitego.js +931 -26
- package/src/installs/gitego.md +1172 -0
- package/src/installs/go.js +913 -26
- package/src/installs/go.md +958 -0
- package/src/installs/google-chrome.js +801 -25
- package/src/installs/google-chrome.md +862 -0
- package/src/installs/gpg.js +412 -73
- package/src/installs/gpg.md +1056 -0
- package/src/installs/homebrew.js +1015 -26
- package/src/installs/homebrew.md +988 -0
- package/src/installs/imageoptim.js +950 -26
- package/src/installs/imageoptim.md +1119 -0
- package/src/installs/installers.json +2297 -0
- package/src/installs/jq.js +382 -26
- package/src/installs/jq.md +809 -0
- package/src/installs/keyboard-maestro.js +701 -26
- package/src/installs/keyboard-maestro.md +825 -0
- package/src/installs/latex.js +771 -26
- package/src/installs/latex.md +1095 -0
- package/src/installs/lftp.js +338 -26
- package/src/installs/lftp.md +907 -0
- package/src/installs/lsb-release.js +346 -0
- package/src/installs/lsb-release.md +814 -0
- package/src/installs/messenger.js +829 -26
- package/src/installs/messenger.md +900 -0
- package/src/installs/microsoft-office.js +550 -26
- package/src/installs/microsoft-office.md +760 -0
- package/src/installs/microsoft-teams.js +782 -25
- package/src/installs/microsoft-teams.md +886 -0
- package/src/installs/node.js +886 -26
- package/src/installs/node.md +1153 -0
- package/src/installs/nordpass.js +698 -26
- package/src/installs/nordpass.md +921 -0
- package/src/installs/nvm.js +977 -26
- package/src/installs/nvm.md +1057 -0
- package/src/installs/openssh.js +734 -64
- package/src/installs/openssh.md +1056 -0
- package/src/installs/pandoc.js +644 -26
- package/src/installs/pandoc.md +1036 -0
- package/src/installs/pinentry.js +492 -26
- package/src/installs/pinentry.md +1142 -0
- package/src/installs/pngyu.js +851 -26
- package/src/installs/pngyu.md +896 -0
- package/src/installs/postman.js +781 -26
- package/src/installs/postman.md +940 -0
- package/src/installs/procps.js +425 -0
- package/src/installs/procps.md +851 -0
- package/src/installs/safari-tech-preview.js +355 -25
- package/src/installs/safari-tech-preview.md +533 -0
- package/src/installs/sfnt2woff.js +640 -26
- package/src/installs/sfnt2woff.md +795 -0
- package/src/installs/shellcheck.js +463 -26
- package/src/installs/shellcheck.md +1005 -0
- package/src/installs/slack.js +722 -25
- package/src/installs/slack.md +865 -0
- package/src/installs/snagit.js +566 -25
- package/src/installs/snagit.md +844 -0
- package/src/installs/software-properties-common.js +372 -0
- package/src/installs/software-properties-common.md +805 -0
- package/src/installs/spotify.js +858 -25
- package/src/installs/spotify.md +901 -0
- package/src/installs/studio-3t.js +803 -26
- package/src/installs/studio-3t.md +918 -0
- package/src/installs/sublime-text.js +780 -25
- package/src/installs/sublime-text.md +914 -0
- package/src/installs/superwhisper.js +687 -25
- package/src/installs/superwhisper.md +630 -0
- package/src/installs/tailscale.js +727 -26
- package/src/installs/tailscale.md +1100 -0
- package/src/installs/tar.js +389 -0
- package/src/installs/tar.md +946 -0
- package/src/installs/termius.js +780 -26
- package/src/installs/termius.md +844 -0
- package/src/installs/terraform.js +761 -26
- package/src/installs/terraform.md +899 -0
- package/src/installs/tidal.js +752 -25
- package/src/installs/tidal.md +864 -0
- package/src/installs/tmux.js +328 -26
- package/src/installs/tmux.md +1030 -0
- package/src/installs/tree.js +393 -26
- package/src/installs/tree.md +833 -0
- package/src/installs/unzip.js +460 -0
- package/src/installs/unzip.md +879 -0
- package/src/installs/vim.js +403 -26
- package/src/installs/vim.md +1040 -0
- package/src/installs/vlc.js +803 -26
- package/src/installs/vlc.md +927 -0
- package/src/installs/vscode.js +825 -26
- package/src/installs/vscode.md +1002 -0
- package/src/installs/wget.js +415 -0
- package/src/installs/wget.md +791 -0
- package/src/installs/whatsapp.js +710 -25
- package/src/installs/whatsapp.md +854 -0
- package/src/installs/winpty.js +352 -0
- package/src/installs/winpty.md +620 -0
- package/src/installs/woff2.js +535 -26
- package/src/installs/woff2.md +977 -0
- package/src/installs/wsl.js +572 -0
- package/src/installs/wsl.md +699 -0
- package/src/installs/xcode-clt.js +520 -0
- package/src/installs/xcode-clt.md +351 -0
- package/src/installs/xcode.js +542 -26
- package/src/installs/xcode.md +573 -0
- package/src/installs/yarn.js +806 -26
- package/src/installs/yarn.md +1074 -0
- package/src/installs/yq.js +636 -26
- package/src/installs/yq.md +944 -0
- package/src/installs/yt-dlp.js +683 -26
- package/src/installs/yt-dlp.md +946 -0
- package/src/installs/yum-utils.js +297 -0
- package/src/installs/yum-utils.md +648 -0
- package/src/installs/zoom.js +740 -25
- package/src/installs/zoom.md +884 -0
- package/src/scripts/README.md +567 -45
- package/src/scripts/STATUS.md +208 -0
- package/src/scripts/afk.js +395 -7
- package/src/scripts/backup-all.js +731 -9
- package/src/scripts/backup-source.js +711 -8
- package/src/scripts/brewd.js +373 -7
- package/src/scripts/brewi.js +505 -9
- package/src/scripts/brewr.js +512 -9
- package/src/scripts/brews.js +462 -9
- package/src/scripts/brewu.js +488 -7
- package/src/scripts/c.js +185 -7
- package/src/scripts/ccurl.js +325 -8
- package/src/scripts/certbot-crontab-init.js +488 -8
- package/src/scripts/certbot-init.js +641 -9
- package/src/scripts/ch.js +339 -7
- package/src/scripts/claude-danger.js +253 -8
- package/src/scripts/clean-dev.js +419 -8
- package/src/scripts/clear-dns-cache.js +525 -7
- package/src/scripts/clone.js +417 -7
- package/src/scripts/code-all.js +420 -7
- package/src/scripts/count-files.js +195 -8
- package/src/scripts/count-folders.js +195 -8
- package/src/scripts/count.js +248 -8
- package/src/scripts/d.js +203 -7
- package/src/scripts/datauri.js +373 -8
- package/src/scripts/delete-files.js +363 -7
- package/src/scripts/docker-clean.js +410 -8
- package/src/scripts/dp.js +426 -7
- package/src/scripts/e.js +375 -9
- package/src/scripts/empty-trash.js +497 -7
- package/src/scripts/evm.js +428 -9
- package/src/scripts/fetch-github-repos.js +441 -10
- package/src/scripts/get-channel.js +329 -8
- package/src/scripts/get-course.js +384 -11
- package/src/scripts/get-dependencies.js +290 -9
- package/src/scripts/get-folder.js +783 -10
- package/src/scripts/get-tunes.js +411 -10
- package/src/scripts/get-video.js +352 -9
- package/src/scripts/git-backup.js +561 -9
- package/src/scripts/git-clone.js +477 -9
- package/src/scripts/git-pup.js +303 -7
- package/src/scripts/git-push.js +380 -8
- package/src/scripts/h.js +607 -9
- package/src/scripts/hide-desktop-icons.js +483 -7
- package/src/scripts/hide-hidden-files.js +522 -7
- package/src/scripts/install-dependencies-from.js +440 -9
- package/src/scripts/ips.js +647 -10
- package/src/scripts/iso.js +354 -8
- package/src/scripts/killni.js +561 -7
- package/src/scripts/ll.js +451 -8
- package/src/scripts/local-ip.js +310 -8
- package/src/scripts/m.js +508 -8
- package/src/scripts/map.js +293 -8
- package/src/scripts/mkd.js +287 -7
- package/src/scripts/ncu-update-all.js +441 -8
- package/src/scripts/nginx-init.js +702 -12
- package/src/scripts/npmi.js +366 -7
- package/src/scripts/o.js +495 -8
- package/src/scripts/org-by-date.js +321 -7
- package/src/scripts/p.js +208 -7
- package/src/scripts/packages.js +313 -8
- package/src/scripts/path.js +209 -7
- package/src/scripts/ports.js +582 -8
- package/src/scripts/q.js +290 -8
- package/src/scripts/refresh-files.js +378 -10
- package/src/scripts/remove-smaller-files.js +500 -8
- package/src/scripts/rename-files-with-date.js +517 -9
- package/src/scripts/resize-image.js +523 -9
- package/src/scripts/rm-safe.js +653 -8
- package/src/scripts/s.js +525 -9
- package/src/scripts/set-git-public.js +349 -7
- package/src/scripts/show-desktop-icons.js +459 -7
- package/src/scripts/show-hidden-files.js +456 -7
- package/src/scripts/tpa.js +265 -8
- package/src/scripts/tpo.js +264 -7
- package/src/scripts/u.js +489 -7
- package/src/scripts/vpush.js +422 -8
- package/src/scripts/y.js +267 -7
- package/src/utils/common/os.js +94 -2
- package/src/utils/ubuntu/apt.js +13 -7
- package/src/utils/windows/choco.js +82 -26
- package/src/utils/windows/winget.js +89 -27
|
@@ -1,24 +1,516 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* remove-smaller-files - Compare directories and remove smaller duplicates
|
|
5
|
+
*
|
|
6
|
+
* Migrated from legacy dotfiles alias.
|
|
7
|
+
* Original:
|
|
8
|
+
* remove_smaller_files(){
|
|
9
|
+
* LEFT_DIR="$PWD"
|
|
10
|
+
* RIGHT_DIR="$*"
|
|
11
|
+
* echo "LEFT : $LEFT_DIR"
|
|
12
|
+
* echo "RIGHT: $RIGHT_DIR"
|
|
13
|
+
* files="$(find -L "$LEFT_DIR" -type f)"
|
|
14
|
+
* echo "$files" | while read file; do
|
|
15
|
+
* FILE_NAME=${file#$LEFT_DIR}
|
|
16
|
+
* LEFT_FILE="$file"
|
|
17
|
+
* RIGHT_FILE="$RIGHT_DIR""$FILE_NAME"
|
|
18
|
+
* if [ -f "$LEFT_FILE" ]; then
|
|
19
|
+
* if [ -f "$RIGHT_FILE" ]; then
|
|
20
|
+
* LEFT_SIZE=( $( ls -Lon "$LEFT_FILE" ) )
|
|
21
|
+
* LEFT_BYTES=${LEFT_SIZE[3]}
|
|
22
|
+
* RIGHT_SIZE=( $( ls -Lon "$RIGHT_FILE" ) )
|
|
23
|
+
* RIGHT_BYTES=${RIGHT_SIZE[3]}
|
|
24
|
+
* if [ "$LEFT_BYTES" -gt "$RIGHT_BYTES" ]; then
|
|
25
|
+
* echo "REMOVED: $RIGHT_FILE"
|
|
26
|
+
* eval "rm \"$RIGHT_FILE\""
|
|
27
|
+
* elif [ "$RIGHT_BYTES" -gt "$LEFT_BYTES" ]; then
|
|
28
|
+
* echo "REMOVED: $LEFT_FILE"
|
|
29
|
+
* eval "rm \"$LEFT_FILE\""
|
|
30
|
+
* else
|
|
31
|
+
* echo "SKIPPED: $FILE_NAME (same size)"
|
|
32
|
+
* fi
|
|
33
|
+
* fi
|
|
34
|
+
* fi
|
|
35
|
+
* done
|
|
36
|
+
* }
|
|
37
|
+
*
|
|
38
|
+
* This script compares files between two directories (the current directory
|
|
39
|
+
* and a comparison directory). For each file that exists in both locations,
|
|
40
|
+
* it removes the smaller version, keeping the larger one. If both files are
|
|
41
|
+
* the same size, neither is removed.
|
|
42
|
+
*
|
|
43
|
+
* Use cases:
|
|
44
|
+
* - Deduplicating backups where you want to keep the most complete version
|
|
45
|
+
* - Cleaning up after partial file transfers
|
|
46
|
+
* - Comparing original vs compressed versions and removing the smaller one
|
|
47
|
+
*
|
|
48
|
+
* Usage:
|
|
49
|
+
* remove-smaller-files /path/to/comparison/directory
|
|
50
|
+
*
|
|
51
|
+
* The script will:
|
|
52
|
+
* 1. Scan all files in the current directory (recursively)
|
|
53
|
+
* 2. For each file, check if a matching file exists in the comparison directory
|
|
54
|
+
* 3. If both exist, compare sizes and remove the smaller one
|
|
55
|
+
* 4. If sizes are equal, skip (no removal)
|
|
56
|
+
*
|
|
5
57
|
* @module scripts/remove-smaller-files
|
|
6
58
|
*/
|
|
7
59
|
|
|
60
|
+
const os = require('../utils/common/os');
|
|
61
|
+
const fs = require('fs');
|
|
62
|
+
const path = require('path');
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Recursively collects all files in a directory.
|
|
66
|
+
*
|
|
67
|
+
* This function walks the entire directory tree and returns the paths
|
|
68
|
+
* of all regular files found. Symbolic links are followed (like the
|
|
69
|
+
* original bash function using `find -L`).
|
|
70
|
+
*
|
|
71
|
+
* @param {string} dir - The directory to scan (absolute path)
|
|
72
|
+
* @param {string} baseDir - The base directory for computing relative paths
|
|
73
|
+
* @returns {string[]} Array of relative file paths from the base directory
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* const files = getAllFiles('/home/user/project', '/home/user/project');
|
|
77
|
+
* // Returns: ['README.md', 'src/index.js', 'src/utils/helper.js']
|
|
78
|
+
*/
|
|
79
|
+
function getAllFiles(dir, baseDir) {
|
|
80
|
+
const results = [];
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Inner recursive function that walks the directory tree.
|
|
84
|
+
* @param {string} currentDir - The current directory being processed
|
|
85
|
+
*/
|
|
86
|
+
function walk(currentDir) {
|
|
87
|
+
let entries;
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
// Read directory contents with file type information
|
|
91
|
+
entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
92
|
+
} catch (error) {
|
|
93
|
+
// Handle permission errors or other read failures gracefully
|
|
94
|
+
// Common reasons: permission denied, directory deleted during scan
|
|
95
|
+
if (error.code !== 'ENOENT' && error.code !== 'EACCES') {
|
|
96
|
+
console.error(`Warning: Could not read directory ${currentDir}: ${error.message}`);
|
|
97
|
+
}
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
for (const entry of entries) {
|
|
102
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
// Use stat (not lstat) to follow symbolic links like `find -L`
|
|
106
|
+
const stats = fs.statSync(fullPath);
|
|
107
|
+
|
|
108
|
+
if (stats.isDirectory()) {
|
|
109
|
+
// Recursively walk subdirectories
|
|
110
|
+
walk(fullPath);
|
|
111
|
+
} else if (stats.isFile()) {
|
|
112
|
+
// Compute the relative path from the base directory
|
|
113
|
+
const relativePath = path.relative(baseDir, fullPath);
|
|
114
|
+
results.push(relativePath);
|
|
115
|
+
}
|
|
116
|
+
} catch (error) {
|
|
117
|
+
// Skip files we cannot stat (broken symlinks, permission issues)
|
|
118
|
+
if (error.code !== 'ENOENT' && error.code !== 'EACCES') {
|
|
119
|
+
console.error(`Warning: Could not access ${fullPath}: ${error.message}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
walk(dir);
|
|
126
|
+
return results;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Gets the size of a file in bytes.
|
|
131
|
+
*
|
|
132
|
+
* @param {string} filePath - Absolute path to the file
|
|
133
|
+
* @returns {number|null} Size in bytes, or null if file cannot be accessed
|
|
134
|
+
*/
|
|
135
|
+
function getFileSize(filePath) {
|
|
136
|
+
try {
|
|
137
|
+
const stats = fs.statSync(filePath);
|
|
138
|
+
return stats.size;
|
|
139
|
+
} catch (error) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Formats file size for human-readable output.
|
|
146
|
+
*
|
|
147
|
+
* Converts bytes to a human-readable string with appropriate units
|
|
148
|
+
* (B, KB, MB, GB).
|
|
149
|
+
*
|
|
150
|
+
* @param {number} bytes - The size in bytes
|
|
151
|
+
* @returns {string} Human-readable size string
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* formatSize(1024); // "1.0 KB"
|
|
155
|
+
* formatSize(1048576); // "1.0 MB"
|
|
156
|
+
*/
|
|
157
|
+
function formatSize(bytes) {
|
|
158
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
159
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
160
|
+
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
161
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Safely removes a file.
|
|
166
|
+
*
|
|
167
|
+
* @param {string} filePath - Absolute path to the file to remove
|
|
168
|
+
* @returns {boolean} True if successfully removed, false otherwise
|
|
169
|
+
*/
|
|
170
|
+
function removeFile(filePath) {
|
|
171
|
+
try {
|
|
172
|
+
fs.unlinkSync(filePath);
|
|
173
|
+
return true;
|
|
174
|
+
} catch (error) {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Pure Node.js implementation that works on any platform.
|
|
181
|
+
*
|
|
182
|
+
* This function uses only Node.js built-in modules (fs, path) to:
|
|
183
|
+
* 1. Scan all files in the current directory (left directory)
|
|
184
|
+
* 2. For each file, check if it exists in the comparison directory (right directory)
|
|
185
|
+
* 3. Compare file sizes and remove the smaller version
|
|
186
|
+
* 4. Report statistics about what was removed
|
|
187
|
+
*
|
|
188
|
+
* This approach is preferred over shelling out to `find` and `ls` because:
|
|
189
|
+
* - It works identically on all platforms (macOS, Linux, Windows)
|
|
190
|
+
* - Node.js fs operations are fast and reliable
|
|
191
|
+
* - We avoid shell escaping and quoting issues with special characters in filenames
|
|
192
|
+
* - Better error handling and reporting
|
|
193
|
+
*
|
|
194
|
+
* The algorithm:
|
|
195
|
+
* - LEFT directory = current working directory
|
|
196
|
+
* - RIGHT directory = the comparison directory provided as argument
|
|
197
|
+
* - For each file in LEFT that also exists in RIGHT:
|
|
198
|
+
* - If LEFT is larger: remove RIGHT
|
|
199
|
+
* - If RIGHT is larger: remove LEFT
|
|
200
|
+
* - If same size: skip (no removal)
|
|
201
|
+
*
|
|
202
|
+
* @param {string[]} args - Command line arguments
|
|
203
|
+
* @param {string} args[0] - Path to the comparison directory (required)
|
|
204
|
+
* @returns {Promise<void>}
|
|
205
|
+
*/
|
|
206
|
+
async function do_remove_smaller_files_nodejs(args) {
|
|
207
|
+
// The comparison directory is required as the first argument
|
|
208
|
+
const rightDirArg = args[0];
|
|
209
|
+
|
|
210
|
+
if (!rightDirArg) {
|
|
211
|
+
console.error('Error: Comparison directory path is required.');
|
|
212
|
+
console.error('');
|
|
213
|
+
console.error('Usage: remove-smaller-files /path/to/comparison/directory');
|
|
214
|
+
console.error('');
|
|
215
|
+
console.error('This command compares files in the current directory with files');
|
|
216
|
+
console.error('in the comparison directory. For each file that exists in both');
|
|
217
|
+
console.error('locations, it removes the smaller version.');
|
|
218
|
+
process.exit(1);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Resolve paths to absolute paths
|
|
222
|
+
const leftDir = process.cwd();
|
|
223
|
+
const rightDir = path.resolve(rightDirArg);
|
|
224
|
+
|
|
225
|
+
// Verify the comparison directory exists
|
|
226
|
+
try {
|
|
227
|
+
const stats = fs.statSync(rightDir);
|
|
228
|
+
if (!stats.isDirectory()) {
|
|
229
|
+
console.error(`Error: '${rightDir}' is not a directory.`);
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
} catch (error) {
|
|
233
|
+
if (error.code === 'ENOENT') {
|
|
234
|
+
console.error(`Error: Comparison directory '${rightDir}' does not exist.`);
|
|
235
|
+
} else {
|
|
236
|
+
console.error(`Error: Cannot access comparison directory '${rightDir}': ${error.message}`);
|
|
237
|
+
}
|
|
238
|
+
process.exit(1);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Print the directories being compared (like the original)
|
|
242
|
+
console.log(`LEFT : ${leftDir}`);
|
|
243
|
+
console.log(`RIGHT: ${rightDir}`);
|
|
244
|
+
console.log('');
|
|
245
|
+
|
|
246
|
+
// Scan all files in the left directory
|
|
247
|
+
console.log('Scanning for files...');
|
|
248
|
+
const leftFiles = getAllFiles(leftDir, leftDir);
|
|
249
|
+
|
|
250
|
+
if (leftFiles.length === 0) {
|
|
251
|
+
console.log('No files found in the current directory.');
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
console.log(`Found ${leftFiles.length} file(s) in left directory.`);
|
|
256
|
+
console.log('');
|
|
257
|
+
console.log('Comparing files and removing smaller versions...');
|
|
258
|
+
console.log('');
|
|
259
|
+
|
|
260
|
+
// Statistics tracking
|
|
261
|
+
let comparedCount = 0;
|
|
262
|
+
let skippedSameSizeCount = 0;
|
|
263
|
+
let removedFromLeftCount = 0;
|
|
264
|
+
let removedFromRightCount = 0;
|
|
265
|
+
let removedFromLeftBytes = 0;
|
|
266
|
+
let removedFromRightBytes = 0;
|
|
267
|
+
let onlyInLeftCount = 0;
|
|
268
|
+
let errorCount = 0;
|
|
269
|
+
|
|
270
|
+
// Process each file
|
|
271
|
+
for (const relativePath of leftFiles) {
|
|
272
|
+
const leftFile = path.join(leftDir, relativePath);
|
|
273
|
+
const rightFile = path.join(rightDir, relativePath);
|
|
274
|
+
|
|
275
|
+
// Check if the file exists in the right directory
|
|
276
|
+
const rightSize = getFileSize(rightFile);
|
|
277
|
+
if (rightSize === null) {
|
|
278
|
+
// File only exists in left directory - nothing to compare
|
|
279
|
+
onlyInLeftCount++;
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Get the size of the left file
|
|
284
|
+
const leftSize = getFileSize(leftFile);
|
|
285
|
+
if (leftSize === null) {
|
|
286
|
+
// Left file disappeared during processing (rare)
|
|
287
|
+
errorCount++;
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
comparedCount++;
|
|
292
|
+
|
|
293
|
+
// Compare sizes and decide which to remove
|
|
294
|
+
if (leftSize > rightSize) {
|
|
295
|
+
// Left is larger - remove the smaller right file
|
|
296
|
+
const success = removeFile(rightFile);
|
|
297
|
+
if (success) {
|
|
298
|
+
console.log(`REMOVED: ${rightFile} (${formatSize(rightSize)} < ${formatSize(leftSize)})`);
|
|
299
|
+
removedFromRightCount++;
|
|
300
|
+
removedFromRightBytes += rightSize;
|
|
301
|
+
} else {
|
|
302
|
+
console.error(`ERROR: Could not remove ${rightFile}`);
|
|
303
|
+
errorCount++;
|
|
304
|
+
}
|
|
305
|
+
} else if (rightSize > leftSize) {
|
|
306
|
+
// Right is larger - remove the smaller left file
|
|
307
|
+
const success = removeFile(leftFile);
|
|
308
|
+
if (success) {
|
|
309
|
+
console.log(`REMOVED: ${leftFile} (${formatSize(leftSize)} < ${formatSize(rightSize)})`);
|
|
310
|
+
removedFromLeftCount++;
|
|
311
|
+
removedFromLeftBytes += leftSize;
|
|
312
|
+
} else {
|
|
313
|
+
console.error(`ERROR: Could not remove ${leftFile}`);
|
|
314
|
+
errorCount++;
|
|
315
|
+
}
|
|
316
|
+
} else {
|
|
317
|
+
// Same size - skip (like the original)
|
|
318
|
+
console.log(`SKIPPED: ${relativePath} (same size: ${formatSize(leftSize)})`);
|
|
319
|
+
skippedSameSizeCount++;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Print summary
|
|
324
|
+
console.log('');
|
|
325
|
+
console.log('--- Summary ---');
|
|
326
|
+
console.log(`Files compared: ${comparedCount}`);
|
|
327
|
+
console.log(`Files only in left directory: ${onlyInLeftCount}`);
|
|
328
|
+
console.log(`Skipped (same size): ${skippedSameSizeCount}`);
|
|
329
|
+
console.log(`Removed from LEFT: ${removedFromLeftCount} (${formatSize(removedFromLeftBytes)})`);
|
|
330
|
+
console.log(`Removed from RIGHT: ${removedFromRightCount} (${formatSize(removedFromRightBytes)})`);
|
|
331
|
+
const totalRemoved = removedFromLeftCount + removedFromRightCount;
|
|
332
|
+
const totalBytes = removedFromLeftBytes + removedFromRightBytes;
|
|
333
|
+
console.log(`Total removed: ${totalRemoved} file(s) (${formatSize(totalBytes)})`);
|
|
334
|
+
|
|
335
|
+
if (errorCount > 0) {
|
|
336
|
+
console.log(`Errors: ${errorCount}`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Compares files between current directory and a comparison directory on macOS,
|
|
342
|
+
* removing the smaller version of each duplicate file pair.
|
|
343
|
+
*
|
|
344
|
+
* Uses the pure Node.js implementation since file operations work
|
|
345
|
+
* identically on macOS. No platform-specific code is needed.
|
|
346
|
+
*
|
|
347
|
+
* @param {string[]} args - Command line arguments
|
|
348
|
+
* @returns {Promise<void>}
|
|
349
|
+
*/
|
|
350
|
+
async function do_remove_smaller_files_macos(args) {
|
|
351
|
+
return do_remove_smaller_files_nodejs(args);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Compares files between current directory and a comparison directory on Ubuntu,
|
|
356
|
+
* removing the smaller version of each duplicate file pair.
|
|
357
|
+
*
|
|
358
|
+
* Uses the pure Node.js implementation since file operations work
|
|
359
|
+
* identically on Linux. No platform-specific code is needed.
|
|
360
|
+
*
|
|
361
|
+
* @param {string[]} args - Command line arguments
|
|
362
|
+
* @returns {Promise<void>}
|
|
363
|
+
*/
|
|
364
|
+
async function do_remove_smaller_files_ubuntu(args) {
|
|
365
|
+
return do_remove_smaller_files_nodejs(args);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Compares files between current directory and a comparison directory on Raspberry Pi OS,
|
|
370
|
+
* removing the smaller version of each duplicate file pair.
|
|
371
|
+
*
|
|
372
|
+
* Uses the pure Node.js implementation since file operations work
|
|
373
|
+
* identically on Linux. No platform-specific code is needed.
|
|
374
|
+
*
|
|
375
|
+
* @param {string[]} args - Command line arguments
|
|
376
|
+
* @returns {Promise<void>}
|
|
377
|
+
*/
|
|
378
|
+
async function do_remove_smaller_files_raspbian(args) {
|
|
379
|
+
return do_remove_smaller_files_nodejs(args);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Compares files between current directory and a comparison directory on Amazon Linux,
|
|
384
|
+
* removing the smaller version of each duplicate file pair.
|
|
385
|
+
*
|
|
386
|
+
* Uses the pure Node.js implementation since file operations work
|
|
387
|
+
* identically on Linux. No platform-specific code is needed.
|
|
388
|
+
*
|
|
389
|
+
* @param {string[]} args - Command line arguments
|
|
390
|
+
* @returns {Promise<void>}
|
|
391
|
+
*/
|
|
392
|
+
async function do_remove_smaller_files_amazon_linux(args) {
|
|
393
|
+
return do_remove_smaller_files_nodejs(args);
|
|
394
|
+
}
|
|
395
|
+
|
|
8
396
|
/**
|
|
9
|
-
* Compares files between
|
|
10
|
-
* version of each
|
|
397
|
+
* Compares files between current directory and a comparison directory on Windows (CMD),
|
|
398
|
+
* removing the smaller version of each duplicate file pair.
|
|
399
|
+
*
|
|
400
|
+
* Uses the pure Node.js implementation since file operations work
|
|
401
|
+
* identically on Windows. No platform-specific code is needed.
|
|
11
402
|
*
|
|
12
403
|
* @param {string[]} args - Command line arguments
|
|
13
|
-
* @param {string} args.0 - Path to comparison directory
|
|
14
404
|
* @returns {Promise<void>}
|
|
15
405
|
*/
|
|
16
|
-
async function
|
|
17
|
-
|
|
406
|
+
async function do_remove_smaller_files_cmd(args) {
|
|
407
|
+
return do_remove_smaller_files_nodejs(args);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Compares files between current directory and a comparison directory on Windows PowerShell,
|
|
412
|
+
* removing the smaller version of each duplicate file pair.
|
|
413
|
+
*
|
|
414
|
+
* Uses the pure Node.js implementation since file operations work
|
|
415
|
+
* identically on Windows. No platform-specific code is needed.
|
|
416
|
+
*
|
|
417
|
+
* @param {string[]} args - Command line arguments
|
|
418
|
+
* @returns {Promise<void>}
|
|
419
|
+
*/
|
|
420
|
+
async function do_remove_smaller_files_powershell(args) {
|
|
421
|
+
return do_remove_smaller_files_nodejs(args);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Compares files between current directory and a comparison directory on Git Bash,
|
|
426
|
+
* removing the smaller version of each duplicate file pair.
|
|
427
|
+
*
|
|
428
|
+
* Uses the pure Node.js implementation since file operations work
|
|
429
|
+
* identically regardless of the shell being used. No platform-specific
|
|
430
|
+
* code is needed.
|
|
431
|
+
*
|
|
432
|
+
* @param {string[]} args - Command line arguments
|
|
433
|
+
* @returns {Promise<void>}
|
|
434
|
+
*/
|
|
435
|
+
async function do_remove_smaller_files_gitbash(args) {
|
|
436
|
+
return do_remove_smaller_files_nodejs(args);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Main entry point - detects environment and executes appropriate implementation.
|
|
441
|
+
*
|
|
442
|
+
* The "remove-smaller-files" command compares files between the current directory
|
|
443
|
+
* and a comparison directory. For each file that exists in both locations, it
|
|
444
|
+
* removes the smaller version, keeping the larger one. If both files are the
|
|
445
|
+
* same size, neither is removed.
|
|
446
|
+
*
|
|
447
|
+
* This is useful for:
|
|
448
|
+
* - Deduplicating backups where you want to keep the most complete version
|
|
449
|
+
* - Cleaning up after partial file transfers
|
|
450
|
+
* - Comparing original vs compressed versions and removing the smaller one
|
|
451
|
+
*
|
|
452
|
+
* Usage:
|
|
453
|
+
* remove-smaller-files /path/to/comparison/directory
|
|
454
|
+
*
|
|
455
|
+
* The command is idempotent - running it multiple times produces the same
|
|
456
|
+
* result (files of equal size are never removed, and once a smaller file
|
|
457
|
+
* is removed, subsequent runs will have nothing to compare).
|
|
458
|
+
*
|
|
459
|
+
* WARNING: This command permanently deletes files. Use with caution and
|
|
460
|
+
* consider backing up important data before running.
|
|
461
|
+
*
|
|
462
|
+
* @param {string[]} args - Command line arguments
|
|
463
|
+
* @param {string} args[0] - Path to the comparison directory (required)
|
|
464
|
+
* @returns {Promise<void>}
|
|
465
|
+
*/
|
|
466
|
+
async function do_remove_smaller_files(args) {
|
|
467
|
+
const platform = os.detect();
|
|
468
|
+
|
|
469
|
+
const handlers = {
|
|
470
|
+
'macos': do_remove_smaller_files_macos,
|
|
471
|
+
'ubuntu': do_remove_smaller_files_ubuntu,
|
|
472
|
+
'debian': do_remove_smaller_files_ubuntu,
|
|
473
|
+
'raspbian': do_remove_smaller_files_raspbian,
|
|
474
|
+
'amazon_linux': do_remove_smaller_files_amazon_linux,
|
|
475
|
+
'rhel': do_remove_smaller_files_amazon_linux,
|
|
476
|
+
'fedora': do_remove_smaller_files_ubuntu,
|
|
477
|
+
'linux': do_remove_smaller_files_ubuntu,
|
|
478
|
+
'wsl': do_remove_smaller_files_ubuntu,
|
|
479
|
+
'cmd': do_remove_smaller_files_cmd,
|
|
480
|
+
'windows': do_remove_smaller_files_cmd,
|
|
481
|
+
'powershell': do_remove_smaller_files_powershell,
|
|
482
|
+
'gitbash': do_remove_smaller_files_gitbash
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
const handler = handlers[platform.type];
|
|
486
|
+
if (!handler) {
|
|
487
|
+
console.error(`Platform '${platform.type}' is not supported for this command.`);
|
|
488
|
+
console.error('');
|
|
489
|
+
console.error('Supported platforms:');
|
|
490
|
+
console.error(' - macOS');
|
|
491
|
+
console.error(' - Ubuntu, Debian, and other Linux distributions');
|
|
492
|
+
console.error(' - Raspberry Pi OS');
|
|
493
|
+
console.error(' - Amazon Linux, RHEL, Fedora');
|
|
494
|
+
console.error(' - Windows (CMD, PowerShell, Git Bash)');
|
|
495
|
+
process.exit(1);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
await handler(args);
|
|
18
499
|
}
|
|
19
500
|
|
|
20
|
-
module.exports = {
|
|
501
|
+
module.exports = {
|
|
502
|
+
main: do_remove_smaller_files,
|
|
503
|
+
do_remove_smaller_files,
|
|
504
|
+
do_remove_smaller_files_nodejs,
|
|
505
|
+
do_remove_smaller_files_macos,
|
|
506
|
+
do_remove_smaller_files_ubuntu,
|
|
507
|
+
do_remove_smaller_files_raspbian,
|
|
508
|
+
do_remove_smaller_files_amazon_linux,
|
|
509
|
+
do_remove_smaller_files_cmd,
|
|
510
|
+
do_remove_smaller_files_powershell,
|
|
511
|
+
do_remove_smaller_files_gitbash
|
|
512
|
+
};
|
|
21
513
|
|
|
22
514
|
if (require.main === module) {
|
|
23
|
-
|
|
515
|
+
do_remove_smaller_files(process.argv.slice(2));
|
|
24
516
|
}
|