337 lines
9.6 KiB
JavaScript
337 lines
9.6 KiB
JavaScript
const { exec, spawn } = require('child_process');
|
||
const { promisify } = require('util');
|
||
const path = require('path');
|
||
const fs = require('fs');
|
||
|
||
const execAsync = promisify(exec);
|
||
|
||
/**
|
||
* 检测当前操作系统
|
||
*/
|
||
function getOSPlatform() {
|
||
const platform = process.platform;
|
||
if (platform === 'win32') return 'windows';
|
||
if (platform === 'darwin') return 'macos';
|
||
if (platform === 'linux') return 'linux';
|
||
return 'unknown';
|
||
}
|
||
|
||
/**
|
||
* 检查文件或目录是否存在
|
||
*/
|
||
function checkPathExists(targetPath) {
|
||
try {
|
||
fs.accessSync(targetPath);
|
||
return true;
|
||
} catch (error) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取目录路径(如果是文件则返回所在目录)
|
||
*/
|
||
function getDirectoryPath(targetPath) {
|
||
try {
|
||
const stats = fs.statSync(targetPath);
|
||
if (stats.isFile()) {
|
||
return path.dirname(targetPath);
|
||
}
|
||
if (stats.isDirectory()) {
|
||
return targetPath;
|
||
}
|
||
} catch (error) {
|
||
throw new Error(`路径不存在或无法访问: ${targetPath}`);
|
||
}
|
||
return targetPath;
|
||
}
|
||
|
||
/**
|
||
* 在 Windows 系统打开目录
|
||
*/
|
||
async function openDirectoryWindows(directoryPath) {
|
||
// 确保路径使用反斜杠
|
||
const normalizedPath = directoryPath.replace(/\//g, '\\');
|
||
|
||
console.log(`📁 在文件资源管理器中打开: ${normalizedPath}`);
|
||
|
||
// 方法1: 使用 explorer 命令
|
||
try {
|
||
const command = `explorer "${normalizedPath}"`;
|
||
await execAsync(command);
|
||
console.log('✅ 已启动文件资源管理器');
|
||
return true;
|
||
} catch (error) {
|
||
console.warn('方法1失败:', error.message);
|
||
}
|
||
|
||
// 方法2: 使用 start 命令
|
||
try {
|
||
const command = `start "" "${normalizedPath}"`;
|
||
await execAsync(command);
|
||
console.log('✅ 已使用 start 命令打开');
|
||
return true;
|
||
} catch (error) {
|
||
console.warn('方法2失败:', error.message);
|
||
}
|
||
|
||
throw new Error('无法在Windows系统中打开目录');
|
||
}
|
||
|
||
/**
|
||
* 在 macOS 系统打开目录
|
||
*/
|
||
async function openDirectoryMacOS(directoryPath) {
|
||
console.log(`📁 在访达中打开: ${directoryPath}`);
|
||
|
||
// 方法1: 使用 open 命令
|
||
try {
|
||
const command = `open "${directoryPath}"`;
|
||
await execAsync(command);
|
||
console.log('✅ 已启动访达');
|
||
return true;
|
||
} catch (error) {
|
||
console.warn('方法1失败:', error.message);
|
||
}
|
||
|
||
// 方法2: 使用 open -R 命令(打开包含指定文件的文件夹)
|
||
try {
|
||
const command = `open -R "${directoryPath}"`;
|
||
await execAsync(command);
|
||
console.log('✅ 已使用 open -R 命令打开');
|
||
return true;
|
||
} catch (error) {
|
||
console.warn('方法2失败:', error.message);
|
||
}
|
||
|
||
throw new Error('无法在macOS系统中打开目录');
|
||
}
|
||
|
||
/**
|
||
* 在 Linux 系统打开目录
|
||
*/
|
||
async function openDirectoryLinux(directoryPath) {
|
||
console.log(`📁 在文件管理器中打开: ${directoryPath}`);
|
||
|
||
// 尝试不同的文件管理器
|
||
const fileManagers = [
|
||
'xdg-open', // 标准Linux桌面打开方式
|
||
'nautilus', // GNOME
|
||
'dolphin', // KDE
|
||
'thunar', // XFCE
|
||
'pcmanfm', // LXDE
|
||
'nemo', // Cinnamon
|
||
'caja' // MATE
|
||
];
|
||
|
||
for (const manager of fileManagers) {
|
||
try {
|
||
// 检查文件管理器是否可用
|
||
await execAsync(`which ${manager}`);
|
||
|
||
const command = `${manager} "${directoryPath}"`;
|
||
const child = spawn(manager, [directoryPath], {
|
||
stdio: 'ignore',
|
||
detached: true
|
||
});
|
||
|
||
child.unref();
|
||
console.log(`✅ 已使用 ${manager} 打开目录`);
|
||
return true;
|
||
} catch (error) {
|
||
// 继续尝试下一个文件管理器
|
||
continue;
|
||
}
|
||
}
|
||
|
||
// 最后尝试 xdg-open(最通用的方式)
|
||
try {
|
||
const command = `xdg-open "${directoryPath}"`;
|
||
const child = spawn('xdg-open', [directoryPath], {
|
||
stdio: 'ignore',
|
||
detached: true
|
||
});
|
||
|
||
child.unref();
|
||
console.log('✅ 已使用 xdg-open 打开目录');
|
||
return true;
|
||
} catch (error) {
|
||
throw new Error('无法在Linux系统中打开目录,请确保已安装文件管理器');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 在终端中打开目录(跨平台)
|
||
*/
|
||
async function openDirectoryInTerminal(directoryPath) {
|
||
const os = getOSPlatform();
|
||
const dirPath = getDirectoryPath(directoryPath);
|
||
|
||
console.log(`💻 操作系统: ${os}`);
|
||
console.log(`📂 目标目录: ${dirPath}`);
|
||
|
||
if (!checkPathExists(dirPath)) {
|
||
throw new Error(`路径不存在: ${dirPath}`);
|
||
}
|
||
|
||
switch (os) {
|
||
case 'windows':
|
||
return await openDirectoryWindows(dirPath);
|
||
case 'macos':
|
||
return await openDirectoryMacOS(dirPath);
|
||
case 'linux':
|
||
return await openDirectoryLinux(dirPath);
|
||
default:
|
||
throw new Error(`不支持的操作系统: ${process.platform}`);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 在系统默认终端中打开并切换到目录
|
||
*/
|
||
async function openTerminalInDirectory(directoryPath) {
|
||
const os = getOSPlatform();
|
||
const dirPath = getDirectoryPath(directoryPath);
|
||
|
||
console.log(`🖥️ 在终端中打开目录: ${dirPath}`);
|
||
|
||
if (!checkPathExists(dirPath)) {
|
||
throw new Error(`路径不存在: ${dirPath}`);
|
||
}
|
||
|
||
const commands = {
|
||
windows: `start cmd /K "cd /d "${dirPath}" && echo 当前目录: %cd%"`,
|
||
macos: `osascript -e 'tell application "Terminal" to do script "cd \\\"${dirPath}\\\" && pwd"'`,
|
||
linux: `gnome-terminal --working-directory="${dirPath}" || konsole --workdir "${dirPath}" || xterm -e "cd \\"${dirPath}\\" && bash"`
|
||
};
|
||
|
||
try {
|
||
let command;
|
||
switch (os) {
|
||
case 'windows':
|
||
command = commands.windows;
|
||
break;
|
||
case 'macos':
|
||
command = commands.macos;
|
||
break;
|
||
case 'linux':
|
||
command = commands.linux;
|
||
break;
|
||
default:
|
||
throw new Error('不支持的操作系统');
|
||
}
|
||
|
||
// 使用 spawn 避免命令注入漏洞
|
||
const shell = os === 'windows' ? 'cmd.exe' : '/bin/bash';
|
||
const args = os === 'windows' ? ['/c', command] : ['-c', command];
|
||
|
||
const child = spawn(shell, args, {
|
||
stdio: 'ignore',
|
||
detached: true
|
||
});
|
||
|
||
child.unref();
|
||
console.log('✅ 已启动终端并切换到目录');
|
||
return true;
|
||
} catch (error) {
|
||
console.warn('无法在终端中打开目录:', error.message);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 主函数 - 打开文件所在目录
|
||
*/
|
||
async function openFileDirectory(filePath, options = {}) {
|
||
const {
|
||
openInTerminal = false, // 是否在终端中打开
|
||
createIfNotExists = false, // 目录不存在时是否创建
|
||
wait = false // 是否等待进程结束
|
||
} = options;
|
||
|
||
try {
|
||
// 检查路径是否存在
|
||
if (!checkPathExists(filePath)) {
|
||
if (createIfNotExists) {
|
||
const dirPath = path.dirname(filePath);
|
||
fs.mkdirSync(dirPath, { recursive: true });
|
||
console.log(`📁 创建目录: ${dirPath}`);
|
||
} else {
|
||
throw new Error(`路径不存在: ${filePath}`);
|
||
}
|
||
}
|
||
|
||
const directoryPath = getDirectoryPath(filePath);
|
||
|
||
if (openInTerminal) {
|
||
await openTerminalInDirectory(directoryPath);
|
||
} else {
|
||
await openDirectoryInTerminal(directoryPath);
|
||
}
|
||
|
||
return true;
|
||
} catch (error) {
|
||
console.error('❌ 打开目录失败:', error.message);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 使用示例
|
||
*/
|
||
async function main() {
|
||
// 示例1: 打开当前脚本所在目录
|
||
const currentFile = __filename;
|
||
console.log('当前文件:', currentFile);
|
||
|
||
// 方法1: 在文件管理器中打开
|
||
console.log('\n--- 在文件管理器中打开 ---');
|
||
await openFileDirectory(currentFile);
|
||
|
||
// 等待2秒
|
||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||
|
||
// 方法2: 在终端中打开(可选)
|
||
console.log('\n--- 在终端中打开 ---');
|
||
await openFileDirectory(currentFile, { openInTerminal: true });
|
||
}
|
||
|
||
// 命令行使用方式
|
||
if (require.main === module) {
|
||
const args = process.argv.slice(2);
|
||
|
||
if (args.length === 0) {
|
||
// 没有参数,打开当前目录
|
||
openFileDirectory(process.cwd())
|
||
.then(success => {
|
||
if (!success) process.exit(1);
|
||
})
|
||
.catch(error => {
|
||
console.error('错误:', error.message);
|
||
process.exit(1);
|
||
});
|
||
} else {
|
||
const filePath = path.resolve(args[0]);
|
||
const options = {
|
||
openInTerminal: args.includes('--terminal') || args.includes('-t'),
|
||
createIfNotExists: args.includes('--create') || args.includes('-c')
|
||
};
|
||
|
||
openFileDirectory(filePath, options)
|
||
.then(success => {
|
||
if (!success) process.exit(1);
|
||
})
|
||
.catch(error => {
|
||
console.error('错误:', error.message);
|
||
process.exit(1);
|
||
});
|
||
}
|
||
}
|
||
|
||
// 导出函数
|
||
module.exports = {
|
||
openFileDirectory,
|
||
openDirectoryInTerminal,
|
||
openTerminalInDirectory,
|
||
getOSPlatform
|
||
}; |