上一篇:无
下一篇:Dart 执行 shell 命令:如何实现交互式操作?
-
macOS 系统本地如何实现?
使用 macOS 自带的“脚本编辑器”应用,新建脚本:
run_shell.scpt:# 此处设置的命令将使用 root 权限执行 set sudo_script to " cd ~/ touch test-sudo " do shell script sudo_script with prompt "应用名称:设置环境变量……" with administrator privileges # 此处设置的命令将使用当前用户权限执行 set normal_script to " cd ~/ touch test-normal " do shell script normal_script
打开终端,执行如下命令:
osascript run_shell.scpt的路径
-
Dart 中如何实现?
如果使用脚本编辑器,在终端输入以下命令也可以执行:osascript -e 'do shell script "cd ~/;touch test-sudo" with prompt "应用名称:设置环境变量……" with administrator privileges'
但是,如果将上述命令直接写到 dart 中,执行并不生效,怎么处理?继续往下看。
Dart 代码实现步骤
a)将 shell 命令保存为一个 .sh 文件,添加可执行权限。
b)将 osascript 命令保存到另一个 .sh 文件,添加可执行权限。
c)在 dart 代码中使用 Process 运行 shell 命令,去执行 shell_osascript.sh 脚本文件。
此时,系统会弹出权限申请窗口,输入密码就可以执行了。
在 dart 代码中封装一个方法:runByAdmin(String command);
思路如下:
command 参数是需要执行的 shell 命令集合,可以是多条 shell 语句。-
Dart 实现示例代码
main.dart:import 'dart:convert'; import 'dart:io'; import 'package:jdflutter_base/jdf_process_input.dart'; import 'jdf_logger.dart'; class JDFProcess { static const String _tag = 'JDFProcess'; static JDFLogger _logger = JDFLogger.getLogger('JDFProcess'); static String _executable = 'bash'; static String _user = Platform.environment['LOGNAME']; static String get user => _user == '' ? null : _user; static String _user_home = Platform.environment['HOME']; static String get user_home => _user_home == '' ? null : _user_home; static void initUserValues() { if (_user == null) { ProcessResult result = JDFProcess.runSync('echo \$USER'); final String tmp = result?.stdout?.trim()?.replaceAll('\n', '') ?? ''; _user = tmp; } if (_user_home == null) { String tmp = user; if (tmp != null && tmp != '') { _user_home = '/Users/$tmp'; } } } static String _makeCommand(String command) { return command; } static String _getExecutable() { if (Platform.environment != null && Platform.environment.containsKey('SHELL')) { String shell = Platform.environment['SHELL']; if (null != shell) { if (shell.contains('/')) { shell = shell.split('/').last; if (shell != null && shell != '') { _executable = shell; } } } } _logger.logd('#! ' + _executable); return _executable; } static Map<String, String> _getEnviroment(Map<String, String> environment) { if (environment == null) { environment = Platform.environment; } return environment; } static Future<Process> start( String command, { String workingDirectory, List<String> inputFlags, void onStart(int pid), void onData(String data), void onError(String error), String onInput(String error, String flag), void onExit(int code), Map<String, String> environment, bool includeParentEnvironment: true, bool runInShell: false, ProcessStartMode mode: ProcessStartMode.normal, bool withInputListen = false, }) async { _executable = _getExecutable(); environment = _getEnviroment(environment); _logger.logd(environment); Process process = await Process.start( _executable, [ '-l', '-c', _makeCommand(command), ], workingDirectory: workingDirectory, environment: environment, includeParentEnvironment: includeParentEnvironment, mode: mode, ); JDFProcessInput _processInput; if (withInputListen) { _processInput = JDFProcessInput(process); _processInput.run(); } if (null != onStart) { onStart(process.pid); } process.stdout.transform(utf8.decoder).listen((data) { data = data?.trim(); if (data.replaceAll('\n', '') == '') { data = ''; } if (null != onData) { onData(data); } _processInputFlags( process: process, data: data, onInput: onInput, inputFlags: inputFlags, ); }); process.stderr.transform(utf8.decoder).listen((error) { error = error?.trim(); if (error.replaceAll('\n', '') == '') { error = ''; } if (null != onError) { onError(error); } _processInputFlags( process: process, data: error, onInput: onInput, inputFlags: inputFlags, ); }); process.exitCode.then((value) { if (_processInput != null) { _processInput.finish(); _processInput = null; } process = null; if (null != onExit) { onExit(value); } }); return process; } static void _processInputFlags({ Process process, String data, List<String> inputFlags, String onInput(String data, String flags), }) { String flag; List<String> dataList = data.split('\n'); for (int i = 0; i < (inputFlags?.length ?? 0); i++) { String element = inputFlags[i]; for (int n = 0; n < dataList.length; n++) { String temp = dataList[n]; if (temp.endsWith(element) || temp.startsWith(element)) { flag = element; break; } } if (null != flag) { break; } } if (null != flag) { _logger.logd('---> onInput() ' + data, tag: _tag); if (null != onInput) { String input = onInput(data, flag); process.stdin.writeln(input); } } } static Future<ProcessResult> run( String command, { String workingDirectory, Map<String, String> environment, bool includeParentEnvironment: true, bool runInShell: false, Encoding stdoutEncoding: systemEncoding, Encoding stderrEncoding: systemEncoding, }) async { _executable = _getExecutable(); _logger.logd(_executable); environment = _getEnviroment(environment); _logger.logd(environment); ProcessResult result = await Process.run( _executable, [ '-l', '-c', _makeCommand(command), ], workingDirectory: workingDirectory, environment: environment, includeParentEnvironment: includeParentEnvironment, runInShell: runInShell, stdoutEncoding: stdoutEncoding, stderrEncoding: stderrEncoding, ); return result; } static ProcessResult runSync( String command, { String workingDirectory, Map<String, String> environment, bool includeParentEnvironment: true, bool runInShell: false, Encoding stdoutEncoding: systemEncoding, Encoding stderrEncoding: systemEncoding, }) { _executable = _getExecutable(); _logger.logd(_executable); environment = _getEnviroment(environment); _logger.logd(environment); ProcessResult result = Process.runSync( _executable, [ '-l', '-c', _makeCommand(command), ], workingDirectory: workingDirectory, environment: environment, includeParentEnvironment: includeParentEnvironment, runInShell: runInShell, stdoutEncoding: stdoutEncoding, stderrEncoding: stderrEncoding, ); return result; } }
-
Dart 示例代码执行效果
运行程序弹出系统弹窗,输入密码执行。执行完成后自动删除创建的两个 .sh 文件。
上面代码中是在 Home 目录创建一个 test_file 文件,5s 后删除,运行结果如下:$ cd ~/ $ ls -la test_file -rw-r--r-- 1 root staff 0 8 27 01:20 test_file
可以看到,创建的文件所属默认为 root 用户,若有需要,可以在 shell 命令中将创建的文件所属修改为当前用户,修改 shell 命令:
void main(List<String> args) { final String user_home = Platform.environment['HOME']; RootProcessForMacOS.run( '创建一个测试文件……', ''' cd ~/ touch test_file chown \$user_home sleep 5 ''', ).whenComplete(() { RootProcessForMacOS.run( '删除刚才创建的文件...', 'cd ~/;rm -rf test_file;', ); }); }
-
FAQ
- 多个程序同时使用这种方式,如何避免 .sh 文件被覆盖?
答:可以通过 uuid 为 .sh 文件设置唯一名称。 - 使用 Dart 执行 shell 命令时,如何进行与执行过程进行交互?
答:可以参考 [Dart 执行 shell 命令:如何实现交互式操作?
- 多个程序同时使用这种方式,如何避免 .sh 文件被覆盖?
上一篇:无
下一篇:Dart 执行 shell 命令:如何实现交互式操作?