मैं एक स्विफ्ट स्क्रिप्ट में टर्मिनल कमांड कैसे चला सकता हूं? (उदाहरण के लिए xcodebuild)


91

मैं अपनी CI बैश स्क्रिप्ट को तेजी से बदलना चाहता हूं। मैं समझ नहीं इस तरह के रूप में सामान्य टर्मिनल कमांड आह्वान करने के लिए कैसे lsयाxcodebuild

#!/usr/bin/env xcrun swift

import Foundation // Works
println("Test") // Works
ls // Fails
xcodebuild -workspace myApp.xcworkspace // Fails

$ ./script.swift
./script.swift:5:1: error: use of unresolved identifier 'ls'
ls // Fails
^
... etc ....

जवाबों:


138

यदि आप स्विफ्ट कोड में कमांड आउटपुट का उपयोग नहीं करते हैं, तो निम्नलिखित पर्याप्त होगा:

#!/usr/bin/env swift

import Foundation

@discardableResult
func shell(_ args: String...) -> Int32 {
    let task = Process()
    task.launchPath = "/usr/bin/env"
    task.arguments = args
    task.launch()
    task.waitUntilExit()
    return task.terminationStatus
}

shell("ls")
shell("xcodebuild", "-workspace", "myApp.xcworkspace")

अपडेट किया गया: Swift3 / Xcode8 के लिए


3
'NSTask' का नाम बदलकर 'प्रोसेस' कर दिया गया है
Mateusz

4
क्या स्विफ्ट 4 में अभी भी प्रक्रिया () है? मुझे एक अपरिभाषित प्रतीक मिल रहा है। : /
अर्नालदो

1
@ArnaldoCapo यह अभी भी मेरे लिए ठीक काम करता है! यहाँ एक उदाहरण है:#!/usr/bin/env swift import Foundation @discardableResult func shell(_ args: String...) -> Int32 { let task = Process() task.launchPath = "/usr/bin/env" task.arguments = args task.launch() task.waitUntilExit() return task.terminationStatus } shell("ls")
CorPruijs

2
मैंने कोशिश की कि मुझे मिल गया: मैंने कोशिश की कि मुझे मिल गया: i.imgur.com/Ge1OOCG.png
cyber8200

5
प्रक्रिया केवल
macOS

94

यदि आप कमांड लाइन तर्कों का उपयोग "बिल्कुल" करना चाहते हैं जैसा कि आप कमांड लाइन में करेंगे (सभी तर्कों को अलग किए बिना), तो निम्न प्रयास करें।

(यह उत्तर लेगोलेस के उत्तर को सुधारता है और स्विफ्ट 5 में इस्तेमाल किया जा सकता है)

import Foundation

func shell(_ command: String) -> String {
    let task = Process()
    let pipe = Pipe()
    
    task.standardOutput = pipe
    task.arguments = ["-c", command]
    task.launchPath = "/bin/zsh"
    task.launch()
    
    let data = pipe.fileHandleForReading.readDataToEndOfFile()
    let output = String(data: data, encoding: .utf8)!
    
    return output
}

// Example usage:
shell("ls -la")

7
यह उत्तर वास्तव में बहुत अधिक होना चाहिए क्योंकि यह पिछले वाले कई मुद्दों को हल करता है।
स्टीवन हेप्टिंग

1
+1। यह ओएसएक्स उपयोगकर्ताओं के लिए ध्यान दिया जाना चाहिए जो /bin/bashसंदर्भित करता है bash-3.2। यदि आप बैश की अधिक उन्नत सुविधाओं का उपयोग करना चाहते हैं, तो पथ को बदल दें ( /usr/bin/env bashआमतौर पर एक अच्छा विकल्प है)
Aserre

किसी को भी इस के साथ मदद कर सकता है? तर्क stackoverflow.com/questions/62203978/…
mahdi

34

यहां समस्या यह है कि आप बैश और स्विफ्ट का मिश्रण और मिलान नहीं कर सकते हैं। आप पहले से ही जानते हैं कि कमांड लाइन से स्विफ्ट स्क्रिप्ट को कैसे चलाना है, अब आपको स्विफ्ट में शेल कमांड को निष्पादित करने के तरीकों को जोड़ना होगा। प्रैक्टिकलस्विफ्ट ब्लॉग के सारांश में :

func shell(launchPath: String, arguments: [String]) -> String?
{
    let task = Process()
    task.launchPath = launchPath
    task.arguments = arguments

    let pipe = Pipe()
    task.standardOutput = pipe
    task.launch()

    let data = pipe.fileHandleForReading.readDataToEndOfFile()
    let output = String(data: data, encoding: String.Encoding.utf8)

    return output
}

निम्न स्विफ्ट कोड xcodebuildतर्कों के साथ निष्पादित होगा और फिर परिणाम का उत्पादन करेगा।

shell("xcodebuild", ["-workspace", "myApp.xcworkspace"]);

जैसा कि निर्देशिका सामग्री (जो lsबाश में क्या करता है) की खोज के लिए, मैं NSFileManagerसीधे बैश आउटपुट के बजाय स्विफ्ट में निर्देशिका का उपयोग और स्कैन करने का सुझाव देता हूं , जो पार्स करने के लिए दर्द हो सकता है।


1
महान - मैंने इस संकलन को बनाने के लिए कुछ संपादन किए हैं, हालांकि मुझे आमंत्रित करने के लिए एक रनटाइम अपवाद मिल रहा है shell("ls", [])- 'NSInvalidArgumentException', reason: 'launch path not accessible' कोई विचार?
राबर्ट

5
NSTask निष्पादन योग्य (पर्यावरण से अपने पथ का उपयोग करके) खोज नहीं करता है जैसा कि शेल करता है। लॉन्च पथ एक पूर्ण पथ (उदाहरण के लिए "/ बिन / ls") या वर्तमान कार्य निर्देशिका के सापेक्ष पथ होना चाहिए।
मार्टिन आर

stackoverflow.com/questions/386783/… PATH मूल रूप से एक शेल की अवधारणा है और उपलब्ध नहीं है।
१२:४४ पर लेगोलिस

महान - यह अब काम करता है। मैंने पूर्ण स्क्रिप्ट + पूर्णता के लिए कुछ संशोधन पोस्ट किए। धन्यवाद।
रॉबर्ट

2
शेल ("सीडी", "~ / डेस्कटॉप /") का उपयोग करते हुए, मुझे मिलता है: / usr / bin / cd: पंक्ति 4: cd: ~ / डेस्कटॉप /: ऐसी कोई फ़ाइल या निर्देशिका नहीं
Zaporozhchenko Oleksandr

23

उपयोगिता समारोह स्विफ्ट 3.0 में

यह कार्य समाप्ति की स्थिति भी देता है और पूरा होने की प्रतीक्षा करता है।

func shell(launchPath: String, arguments: [String] = []) -> (String? , Int32) {
    let task = Process()
    task.launchPath = launchPath
    task.arguments = arguments

    let pipe = Pipe()
    task.standardOutput = pipe
    task.standardError = pipe
    task.launch()
    let data = pipe.fileHandleForReading.readDataToEndOfFile()
    let output = String(data: data, encoding: .utf8)
    task.waitUntilExit()
    return (output, task.terminationStatus)
}

5
import Foundationयाद आ रही है
Binarian

3
अफसोस की बात है, आईओएस के लिए नहीं।
राफेल

17

यदि आप कॉलिंग कमांड के लिए बैश वातावरण का उपयोग करना चाहते हैं, तो निम्नलिखित बैश फ़ंक्शन का उपयोग करें, जो लेगोलेस के निश्चित संस्करण का उपयोग करता है। मुझे शेल फ़ंक्शन के परिणाम से एक अनुगामी न्यूलाइन निकालना था।

स्विफ्ट 3.0: (Xcode8)

import Foundation

func shell(launchPath: String, arguments: [String]) -> String
{
    let task = Process()
    task.launchPath = launchPath
    task.arguments = arguments

    let pipe = Pipe()
    task.standardOutput = pipe
    task.launch()

    let data = pipe.fileHandleForReading.readDataToEndOfFile()
    let output = String(data: data, encoding: String.Encoding.utf8)!
    if output.characters.count > 0 {
        //remove newline character.
        let lastIndex = output.index(before: output.endIndex)
        return output[output.startIndex ..< lastIndex]
    }
    return output
}

func bash(command: String, arguments: [String]) -> String {
    let whichPathForCommand = shell(launchPath: "/bin/bash", arguments: [ "-l", "-c", "which \(command)" ])
    return shell(launchPath: whichPathForCommand, arguments: arguments)
}

उदाहरण के लिए वर्तमान वर्किंग डाइरेक्टरी के वर्तमान वर्किंग गेट शाखा को पाने के लिए:

let currentBranch = bash("git", arguments: ["describe", "--contains", "--all", "HEAD"])
print("current branch:\(currentBranch)")

13

लेगोलस के उत्तर पर आधारित पूर्ण स्क्रिप्ट

#!/usr/bin/env swift

import Foundation

func printShell(launchPath: String, arguments: [String] = []) {
    let output = shell(launchPath: launchPath, arguments: arguments)

    if (output != nil) {
        print(output!)
    }
}

func shell(launchPath: String, arguments: [String] = []) -> String? {
    let task = Process()
    task.launchPath = launchPath
    task.arguments = arguments

    let pipe = Pipe()
    task.standardOutput = pipe
    task.launch()

    let data = pipe.fileHandleForReading.readDataToEndOfFile()
    let output = String(data: data, encoding: String.Encoding.utf8)

    return output
}

// > ls
// > ls -a -g
printShell(launchPath: "/bin/ls")
printShell(launchPath: "/bin/ls", arguments:["-a", "-g"])

11

बस इसे अपडेट करने के लिए क्योंकि Apple ने .launchPath और लॉन्च () दोनों को अपदस्थ कर दिया है, यहाँ Swift 4 के लिए एक अद्यतन उपयोगिता फ़ंक्शन है जो भविष्य में थोड़ा और अधिक प्रमाण होना चाहिए।

नोट: प्रतिस्थापन पर एप्पल के दस्तावेज ( रन () , एक्जीक्यूटेबल , आदि) मूल रूप से इस बिंदु पर खाली हैं।

import Foundation

// wrapper function for shell commands
// must provide full path to executable
func shell(_ launchPath: String, _ arguments: [String] = []) -> (String?, Int32) {
  let task = Process()
  task.executableURL = URL(fileURLWithPath: launchPath)
  task.arguments = arguments

  let pipe = Pipe()
  task.standardOutput = pipe
  task.standardError = pipe

  do {
    try task.run()
  } catch {
    // handle errors
    print("Error: \(error.localizedDescription)")
  }

  let data = pipe.fileHandleForReading.readDataToEndOfFile()
  let output = String(data: data, encoding: .utf8)

  task.waitUntilExit()
  return (output, task.terminationStatus)
}


// valid directory listing test
let (goodOutput, goodStatus) = shell("/bin/ls", ["-la"])
if let out = goodOutput { print("\(out)") }
print("Returned \(goodStatus)\n")

// invalid test
let (badOutput, badStatus) = shell("ls")

कार्रवाई में इसे देखने के लिए इसे सीधे एक खेल के मैदान में पेस्ट करने में सक्षम होना चाहिए।


8

स्विफ्ट 4.0 के लिए अद्यतन (परिवर्तनों से निपटने String)

func shell(launchPath: String, arguments: [String]) -> String
{
    let task = Process()
    task.launchPath = launchPath
    task.arguments = arguments

    let pipe = Pipe()
    task.standardOutput = pipe
    task.launch()

    let data = pipe.fileHandleForReading.readDataToEndOfFile()
    let output = String(data: data, encoding: String.Encoding.utf8)!
    if output.count > 0 {
        //remove newline character.
        let lastIndex = output.index(before: output.endIndex)
        return String(output[output.startIndex ..< lastIndex])
    }
    return output
}

func bash(command: String, arguments: [String]) -> String {
    let whichPathForCommand = shell(launchPath: "/bin/bash", arguments: [ "-l", "-c", "which \(command)" ])
    return shell(launchPath: whichPathForCommand, arguments: arguments)
}

उदाहरण दें
गौतम सोर्यराज

4

यहाँ पोस्ट किए गए कुछ समाधानों की कोशिश करने के बाद, मैंने पाया कि कमांड निष्पादित करने का सबसे अच्छा तरीका -cतर्कों के लिए ध्वज का उपयोग करना था ।

@discardableResult func shell(_ command: String) -> (String?, Int32) {
    let task = Process()

    task.launchPath = "/bin/bash"
    task.arguments = ["-c", command]

    let pipe = Pipe()
    task.standardOutput = pipe
    task.standardError = pipe
    task.launch()

    let data = pipe.fileHandleForReading.readDataToEndOfFile()
    let output = String(data: data, encoding: .utf8)
    task.waitUntilExit()
    return (output, task.terminationStatus)
}


let _ = shell("mkdir ~/Desktop/test")

0

स्विफ्ट 3 के लिए रिंटारो और लेगोलेस के उत्तर मिलाएं

@discardableResult
func shell(_ args: String...) -> String {
    let task = Process()
    task.launchPath = "/usr/bin/env"
    task.arguments = args

    let pipe = Pipe()
    task.standardOutput = pipe

    task.launch()
    task.waitUntilExit()

    let data = pipe.fileHandleForReading.readDataToEndOfFile()

    guard let output: String = String(data: data, encoding: .utf8) else {
        return ""
    }
    return output
}

0

एनवी चर के समर्थन के साथ छोटा सुधार:

func shell(launchPath: String,
           arguments: [String] = [],
           environment: [String : String]? = nil) -> (String , Int32) {
    let task = Process()
    task.launchPath = launchPath
    task.arguments = arguments
    if let environment = environment {
        task.environment = environment
    }

    let pipe = Pipe()
    task.standardOutput = pipe
    task.standardError = pipe
    task.launch()
    let data = pipe.fileHandleForReading.readDataToEndOfFile()
    let output = String(data: data, encoding: .utf8) ?? ""
    task.waitUntilExit()
    return (output, task.terminationStatus)
}

0

पायथन लिपि को चलाने के लिए प्रोसेस क्लास का उपयोग करने का उदाहरण।

इसके अलावा:

 - added basic exception handling
 - setting environment variables (in my case I had to do it to get Google SDK to authenticate correctly)
 - arguments 







 import Cocoa

func shellTask(_ url: URL, arguments:[String], environment:[String : String]) throws ->(String?, String?){
   let task = Process()
   task.executableURL = url
   task.arguments =  arguments
   task.environment = environment

   let outputPipe = Pipe()
   let errorPipe = Pipe()

   task.standardOutput = outputPipe
   task.standardError = errorPipe
   try task.run()

   let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile()
   let errorData = errorPipe.fileHandleForReading.readDataToEndOfFile()

   let output = String(decoding: outputData, as: UTF8.self)
   let error = String(decoding: errorData, as: UTF8.self)

   return (output,error)
}

func pythonUploadTask()
{
   let url = URL(fileURLWithPath: "/usr/bin/python")
   let pythonScript =  "upload.py"

   let fileToUpload = "/CuteCat.mp4"
   let arguments = [pythonScript,fileToUpload]
   var environment = ProcessInfo.processInfo.environment
   environment["PATH"]="usr/local/bin"
   environment["GOOGLE_APPLICATION_CREDENTIALS"] = "/Users/j.chudzynski/GoogleCredentials/credentials.json"
   do {
      let result = try shellTask(url, arguments: arguments, environment: environment)
      if let output = result.0
      {
         print(output)
      }
      if let output = result.1
      {
         print(output)
      }

   } catch  {
      print("Unexpected error:\(error)")
   }
}

आप "अपलोड थ्रीडी" फ़ाइल को कहाँ रखते हैं
सुहैब रूम

0

मैंने SwiftExec बनाया है , इस तरह के कमांड चलाने के लिए एक छोटा पुस्तकालय:

import SwiftExec

var result: ExecResult
do {
    result = try exec(program: "/usr/bin/git", arguments: ["status"])
} catch {
    let error = error as! ExecError
    result = error.execResult
}

print(result.exitCode!)
print(result.stdout!)
print(result.stderr!)

यह एक एकल-फ़ाइल लाइब्रेरी है जिसे आसानी से परियोजनाओं में कॉपी-पेस्ट किया जा सकता है या एसपीएम का उपयोग करके स्थापित किया जा सकता है। यह परीक्षण किया गया है और त्रुटि से निपटने को सरल करता है।

इसमें शेलऑट भी है , जो अतिरिक्त रूप से पूर्व-परिभाषित कमांड की एक किस्म का समर्थन करता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.