Socket
Socket
Sign inDemoInstall

hlink

Package Overview
Dependencies
Maintainers
1
Versions
82
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

hlink - npm Package Compare versions

Comparing version 0.2.1 to 0.2.2

CHANGELOG.md

30

cli.js

@@ -6,2 +6,6 @@ #!/usr/bin/env node

const hardLink = require("./lib/index");
const exts = require('./lib/exts')([]);
const inquirer = require('inquirer');
const path = require('path')
const fs = require('fs-extra')

@@ -14,11 +18,12 @@ const cli = meow(

可配置选项:
--saveLevel,-l [Default: 0]
${chalk.gray(`saveLevel=2 只保存文件
saveLevel=1 保存一级目录
saveLevel=0 保存原有的相对源地址的路径`)}
--ext,-e [Default: mkv,mp4,rmvb]
--maxFindLevel,-m [Default: 4] 删除硬链
--delete,-d 删除目标地址所有硬链
${chalk.gray(`delete=1 表示删除目录
delete=0 表示只删除文件`)}
--saveMode,-s 保存模式,默认为模式0
${chalk.gray(`saveMode=1 保存一级目录
saveMode=0 保存原有的相对源地址的路径`)}
--ext,-e 额外需要做外链扩展名
${chalk.gray(`默认包含了常用了的视频扩展名: ${exts.join(',')}`)}
--maxFindLevel,-m 查找文件的最大层级, 默认4层
--delete,-d 删除目标地址所有硬链,默认为false
例子:

@@ -29,5 +34,4 @@ ${chalk.grey(`# 创建 /share/download 下面文件到目标地址 /share/movie`)}

${chalk.grey(`# 删除 /share/download 下面文件在 /share/movie 下面的对应硬链的文件夹`)}
$ hlink -d=1 /share/download /share/movie
$ hlink -d /share/download /share/movie
说明:

@@ -46,3 +50,3 @@ 1. 创建硬链时,会自动检测硬链接是否存在,硬链改名同样能检测到

type: "string",
default: "mkv,mp4,rmvb",
default: "",
alias: "e"

@@ -56,3 +60,3 @@ },

delete: {
type: 'string',
type: 'boolean',
alias: 'd'

@@ -59,0 +63,0 @@ }

@@ -15,2 +15,6 @@ const fs = require("fs-extra");

} = require('./utils');
const getExts = require('./exts');
const updateSourceAndDest = require("./updateSourceAndDest");
const configPath = require("./configPath");
const inquirer = require('inquirer');

@@ -21,28 +25,111 @@ const resolvePath = path.resolve;

function getRealDestPath(sourcePath, destPath, saveLevels, startPath, s) {
if (saveLevels !== 2) {
const relativePath = path
.relative(startPath, path.resolve(sourcePath)) || s.replace(path.extname(s), '');
return path.resolve(
destPath,
relativePath
.split(path.sep)
.slice(-saveLevels)
.join(path.sep)
);
}
const relativePath = path
.relative(startPath, path.resolve(sourcePath)) || s.replace(path.extname(s), '');
return path.resolve(
destPath,
relativePath
.split(path.sep)
.slice(-saveLevels)
.join(path.sep)
);
return destPath;
}
function hardLink(input, options) {
warning(!input.length, "必须指定目标地址");
let source = resolvePath(process.cwd());
let dest = false;
function getSource(answers) {
const isSecondDir = answers.sourcePath === '二级目录';
const finalSourceDir = isSecondDir ? path.join(answers.sourcePathSecond, answers.secondDir) : answers.sourcePath;
const sourceDir = isSecondDir ? path.dirname(finalSourceDir) : finalSourceDir;
return [finalSourceDir, sourceDir]
}
function parseInput(input) {
if (input.length === 1) {
dest = resolvePath(input[0]);
} else if (input.length === 2) {
source = resolvePath(input[0]);
dest = resolvePath(input[1]);
return [resolvePath(process.cwd()), resolvePath(input[0])]
} else if (input.length >= 2) {
return [resolvePath(input[0]), resolvePath(input[1])]
}
}
async function hardLink(input, options) {
const { s, e, m, d } = options;
let source = false;
let dest = false;
const isDelete = !!d;
let deleteDir = false;
let isSecondDir = false;
let sourceDir = false;
if (!isDelete) {
warning(!input.length, "必须指定目标地址");
const parsedPaths = parseInput(input);
source = parsedPaths[0]
sourceDir = parsedPaths[0]
dest = parsedPaths[1]
} else {
const mapJsonNotExist = !fs.existsSync(configPath.mapJson)
const noSaveRecord = !mapJsonNotExist && !Object.keys(fs.readJSONSync(configPath.mapJson)).length;
warning((mapJsonNotExist || noSaveRecord) && !input.length, "你没有创建记录,你必须手动指定目标地址及源地址来进行删除操作");
const answerDeleteMode = await inquirer.prompt([{
type: 'rawlist',
message: '请选择删除模式?',
name: 'deleteDir',
choices: ['仅仅删除文件', '删除硬链文件以及其所在目录'],
default: '删除硬链文件以及其所在目录'
}])
deleteDir = answerDeleteMode.deleteDir === '删除硬链文件以及其所在目录';
if (input.length) {
const parsedPaths = parseInput(input);
source = parsedPaths[0]
sourceDir = parsedPaths[0]
dest = parsedPaths[1]
} else {
const pathsMap = fs.readJSONSync(configPath.mapJson);
const answers = await inquirer.prompt([{
type: 'rawlist',
name: 'sourcePath',
message: '请选择需要删除硬链的源地址',
choices: Object.keys(pathsMap).concat('二级目录')
}, {
message: '你需要选择哪个文件夹的二级目录',
type: 'rawlist',
name: 'sourcePathSecond',
when: (answer) => {
return answer.sourcePath === '二级目录'
},
choices: Object.keys(pathsMap).map(s => ({
value: s,
name: s + '的二级目录'
}))
}, {
type: 'rawlist',
name: 'secondDir',
message: "请选择一个二级目录作为源地址",
when: (answer) => {
return !!answer.sourcePathSecond
},
choices: (answer) => {
const file = fs.readdirSync(answer.sourcePathSecond).filter((s) => {
return fs.statSync(path.join(answer.sourcePathSecond, s)).isDirectory()
})
return file
}
}, {
type: 'rawlist',
name: 'destDir',
message: "该源地址对应两个目标地址,请选择一个",
when: (answer) => {
const choiceDest = pathsMap[getSource(answer)[1]];
return choiceDest && choiceDest.length > 1;
},
choices: (answer) => {
return pathsMap[getSource(answer)[1]]
}
}])
const [realSource, createdSourceDir] = getSource(answers);
source = realSource;
sourceDir = createdSourceDir
dest = answers.destDir || pathsMap[createdSourceDir][0];
isSecondDir = answers.sourcePath === '二级目录'
}
}
checkDirectory(source, dest);
const { s, e, m, d } = options;
const exts = e.split(",");

@@ -58,3 +145,3 @@ const saveLevels = +s;

};
if (!d) {
if (!isDelete) {
log.info("开始创建硬链...");

@@ -67,3 +154,2 @@ log.info("当前配置为:");

log.info("开始删除硬链...")
log.info(`删除模式为: ${chalk.cyan(d === '0' ? '仅删除文件' : '删除对应目录')}`)
}

@@ -75,3 +161,3 @@ function start(sourcePath, currentLevel = 1) {

const sourceDirContent = fs.readdirSync(sourcePath);
sourceDirContent.forEach(s => {
sourceDirContent.forEach(async s => {
const extname = path.extname(s).replace(".", "");

@@ -81,4 +167,4 @@ const filePath = resolvePath(sourcePath, s);

// 地址继续循环
start(filePath, currentLevel + 1);
} else if (exts.indexOf(extname) > -1) {
await start(filePath, currentLevel + 1);
} else if (getExts(exts).indexOf(extname.toLowerCase()) > -1) {
const realDestPath = getRealDestPath(

@@ -91,18 +177,12 @@ sourcePath,

);
if (!!d) {
if (isDelete) {
// 删除硬链接
try {
const linkPaths = getLinkPath(filePath, realDestPath, d === '1' && dest)
if (!linkPaths.length) {
log.warn(`没有找到 ${chalk.cyan(getDirBasePath(source, filePath))} 硬链接`);
}
const linkPaths = getLinkPath(filePath, dest, deleteDir)
linkPaths.map((removePath) => {
execa.sync('rm', ['-r', removePath])
const deletePathMessage = chalk.cyan(getDirBasePath(dest, removePath));
if (d === '0') {
log.info(`删除硬链文件成功 ${deletePathMessage} `)
} else {
log.info(`目录 ${deletePathMessage} 已删除`)
}
log.info(`${deleteDir ? '目录' : '硬链'} ${deletePathMessage} 已删除`)
})
// }
} catch (e) {

@@ -113,27 +193,28 @@ if (e.message === 'ALREADY_DELETE') {

}
return
}
// 做硬链接
const sourceNameForMessage = chalk.yellow(getDirBasePath(source, filePath));
const destNameForMessage = chalk.cyan(getDirBasePath(dest, path.join(realDestPath, s)));
try {
fs.ensureDirSync(realDestPath);
if (checkLinkExist(filePath, realDestPath)) {
throw { stderr: 'File exists' }
}
execa.sync("ln", [filePath, realDestPath]);
log.success(
`源地址 ${sourceNameForMessage} 硬链成功, 硬链地址为 ${destNameForMessage}`
);
} catch (e) {
if (!e.stderr) {
console.log(e);
process.exit(0);
}
if (e.stderr.indexOf("File exists") === -1) {
console.log(e);
} else {
log.warn(
`源地址"${sourceNameForMessage}"硬链已存在, 跳过创建`
} else {
// 做硬链接
const sourceNameForMessage = chalk.yellow(getDirBasePath(source, filePath));
const destNameForMessage = chalk.cyan(getDirBasePath(dest, path.join(realDestPath, s)));
try {
if (checkLinkExist(filePath, dest)) {
throw { stderr: 'File exists' }
} else {
fs.ensureDirSync(realDestPath);
}
execa.sync("ln", [filePath, realDestPath]);
log.success(
`源地址 ${sourceNameForMessage} 硬链成功, 硬链地址为 ${destNameForMessage}`
);
} catch (e) {
if (!e.stderr) {
console.log(e);
process.exit(0);
}
if (e.stderr.indexOf("File exists") === -1) {
console.log(e);
} else {
log.warn(
`源地址"${sourceNameForMessage}"硬链已存在, 跳过创建`
);
}
}

@@ -145,4 +226,5 @@ }

start(source);
updateSourceAndDest(sourceDir, dest, isDelete && !isSecondDir)
}
module.exports = hardLink;

@@ -28,15 +28,8 @@

function getLinkPath(file, destPath, destDir) {
function getLinkPath(file, destPath, deleteDir) {
const out = execa.sync('ls', ['-i', file]).stdout;
const fileNumber = out.split(' ')[0]
let findOut = false
try {
findOut = execa.sync('find', [destPath, '-inum', fileNumber]).stdout
} catch (e) {
throw new Error('ALREADY_DELETE');
}
if (destDir && !!findOut) {
return [getTopDir(destPath, destDir)];
}
return !!findOut ? findOut.split('\n') : [];
findOut = execa.sync('find', [destPath, '-inum', fileNumber]).stdout
return !!findOut ? findOut.split('\n').map(p => deleteDir ? path.dirname(p) : p) : [];
}

@@ -54,3 +47,3 @@ function checkLinkExist(file, destPath) {

levels > 2 || levels < 0,
"保存的最大层级saveLevel只能设置为0/1/2"
"保存的最大层级saveLevel只能设置为0/1"
);

@@ -57,0 +50,0 @@ }

{
"name": "hlink",
"version": "0.2.1",
"version": "0.2.2",
"description": "hlink",

@@ -33,5 +33,6 @@ "license": "MIT",

"chalk": "^4.1.0",
"execa": "^2.1.0",
"fs-extra": "^9.0.1",
"meow": "^5.0.0",
"execa": "^2.1.0"
"inquirer": "^7.3.3",
"meow": "^5.0.0"
},

@@ -38,0 +39,0 @@ "devDependencies": {

# hlink
![img](./img.png)
## 所需环境:
nodejs 10 以上;node环境安装请自行百度哈
## 安装
```bash
$ npm install -g hlink
# 查看帮助
$ hlink --help
```
## 功能:
1. 批量创建源地址下面所有视频文件 硬链 到目标地址
2. 重复硬链检测,就算硬链接已改名也能检查到
3. 批量删除源地址在目标地址所创建的硬链
## 使用
![使用](./media/ghelp.png)
## 效果截图
![创建](./media/gcreate.png)
![重复创建](./media/gexist.png)
![删除目录](./media/gdeletedir.png)
![删除文件](./media/gdeletefile.png)
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc