Dokumentverzeichnispfad von Xcode Device Simulator


89

In iOS 7 finden Sie das Dokumentenverzeichnis der iOS-Simulatoren in:

/Users/Sabo/Library/Application Support/iPhone Simulator/

In iOS 8 Beta Simulator kann ich das entsprechende Verzeichnis für iOS 8 jedoch nicht im obigen Verzeichnis finden.

Wo ist der Dokumentverzeichnispfad für den iOS 8 Simulator?

Geben Sie hier die Bildbeschreibung ein


Versuchen Sie dies . Es gibt eine App, die direkt das Dokumentverzeichnis des letzten App-Laufs öffnet.
NSRover



Wenn Sie Ihren Bibliotheksordner nicht finden können, ist er wahrscheinlich ausgeblendet. Wenn Sie diesen Befehl in das Terminal eingeben, wird er ausgeblendet: chflags nohidden ~ / Library
simon_smiley

Wenn Sie das Xcode-Plugin mögen, versuchen Sie es mit github.com/onmyway133/XcodeWay , einem Xcode-Plugin, das zu Ihrem Simulatorordner navigieren kann, und vielen anderen Orten
onmyway133

Antworten:


155

Auf meinem Computer lautet der Pfad:

~/Library/Developer/CoreSimulator/Devices/1A8DF360-B0A6-4815-95F3-68A6AB0BCC78/data/Container/Data/Application/

HINWEIS: Wahrscheinlich unterscheiden sich diese langen IDs (dh UDIDs) auf Ihrem Computer.


1
Wenn Ihre App CoreData verwendet, gibt es möglicherweise eine einfachere Möglichkeit, nach dem Ordner zu suchen: stackoverflow.com/a/25937606/1103584
DiscDev

12
Nein, das ist falsch. Es gibt keinen Unterschied zwischen ObjC-Projekten und Swift-Projekten. Dies sind UDIDs (Unique Device IDentifiers), und Sie können Ihre sehen, indem Sie 'xcrun simctl list' ausführen
Jeremy Huddleston Sequoia

@JeremyHuddlestonSequoia, richtig ist oder nicht, das ist , wie die Swift und ein ObjC Projekt auf meinem Computer kopiert wurden. Ich muss nicht erwähnen, dass beide nur im Simulator kompiliert wurden (das war die ursprüngliche Frage). Wenn der Xcode zwischen UDIDs für verschiedene Sprachen unterscheidet und Sie der Meinung sind, dass er nicht korrekt ist, müssen Sie ihn möglicherweise an Apple melden.
Holex

7
Holex, ich sage, dass Ihre Aussage falsch ist, nicht, dass das Design falsch ist (beachten Sie, dass ich tatsächlich der Typ bei Apple bin, der CoreSimulator entworfen hat, also wäre ich wahrscheinlich die erste Person, die dort etwas korrigiert, was ich denke falsch). Die UDIDs entsprechen separaten Geräten. Der einzige Grund, warum Ihre schnellen und objektiven Projekte unterschiedliche UDIDs verwenden, besteht darin, dass Sie Ihre Projekte auf verschiedenen Simulatorgeräten bereitgestellt haben (z. B. zum einen auf dem iPhone 5s und zum anderen auf dem iPhone 6).
Jeremy Huddleston Sequoia

@JeremyHuddlestonSequoia, Sie können meine Antwort jederzeit korrigieren, wenn Sie sich sicher genug fühlen.
Holex

75

NSLogFühren Sie unter dem Code irgendwo in "AppDelegate" Ihr Projekt aus und folgen Sie dem Pfad. Auf diese Weise können Sie leicht zu den Dokumenten gelangen, anstatt zufällig in "~ / Library / Developer / CoreSimulator / Devices /" zu suchen.

Ziel c

NSLog(@"%@",[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]);

Swift
Wenn Sie Swift 1.2 verwenden, verwenden Sie den folgenden Code, der aufgrund des #if #endifBlocks nur in der Entwicklung ausgegeben wird, wenn Sie den Simulator verwenden :

#if arch(i386) || arch(x86_64)
  let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as! NSString
  NSLog("Document Path: %@", documentsPath)
#endif

Kopieren Sie Ihren Pfad von "/ Users / ankur / Library / Developer / CoreSimulator / Devices / 7BA821 ...", gehen Sie zu " Finder " und dann zu " Gehe zu Ordner " oder command+ shift+ gund fügen Sie Ihren Pfad ein. Lassen Sie sich vom Mac zu Ihrem Pfad führen Dokumentenverzeichnis :)


einfach und effizient!
Loretoparisi

43

Schreiben Sie einfach den folgenden Code in AppDelegate -> didFinishLaunchingWithOptions
Objective C.

#if TARGET_IPHONE_SIMULATOR 
// where are you? 
NSLog(@"Documents Directory: %@", [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]); 
#endif 


Swift 2.X.

if let documentsPath = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first?.path {
    print("Documents Directory: " + documentsPath)   
}

Swift 3.X.

#if arch(i386) || arch(x86_64)
    if let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.path {
        print("Documents Directory: \(documentsPath)")
    }
#endif

Swift 4.2

#if targetEnvironment(simulator)
    if let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.path {
        print("Documents Directory: \(documentsPath)")
    }
#endif

Ausgabe
/ Benutzer / mitul_marsonia / Bibliothek / Entwickler / CoreSimulator / Geräte / E701C1E9-FCED-4428-A36F-17B32D32918A / Daten / Container / Daten / Anwendung / 25174F64-7130-4B91-BC41-AC74257CCC6E / Dokumente

Kopieren Sie Ihren Pfad von "/ Users / mitul_marsonia / Library / Developer / CoreSimulator / Devices / E701C1E9-FCED-4428-A36F-17B32D32918A ...", gehen Sie zu "Finder" und dann zu "Gehe zu Ordner" oder befehle + shift + g und Fügen Sie Ihren Pfad ein und lassen Sie sich vom Mac in Ihr Dokumentenverzeichnis führen


Woher wissen Sie, in welchem ​​Ordner Ihre App enthalten ist? Es gibt zu viele Verzeichnisse. Muss ich jedes davon durchsuchen?
KarenAnne


Dies ist ein fest codierter Pfad zu Ihrem eigenen Simulator. Es wird für niemanden anders funktionieren. Tatsächlich ändert sich dieser Pfad auch für Sie, sobald Sie den Simulator erneut ausführen.
NSRover

#if TARGET_IPHONE_SIMULATOR // wo bist du? NSLog (@ "Dokumentenverzeichnis:% @", [[[NSFileManager defaultManager] URLsForDirectory: NSDocumentDirectory inDomains: NSUserDomainMask] lastObject]); #endif
Mitul Marsoniya

16

Ich empfehle eine nette Dienstprogramm-App namens SimPholders, mit der Sie die Dateien und Ordner während der Entwicklung Ihrer iOS-App leicht finden können. Es gibt eine neue Version für die neuen Simulatoren namens SimPholders2. Es kann bei simpholders.com gefunden werden


Das wäre eine coole App, da iOS8 Simulator in der Dunkelheit der Ordner vergraben ist, diese App jedoch nicht aktualisiert wurde, um iOS8 zu unterstützen.
Wibobm

@wibobm. SimPholders2 unterstützt sicherlich iOS 8.0 und iOS 8.1
JDibble

OH MEIN GOTT. Ich muss total blind gewesen sein. Danke dir!
Wibobm

16

Trotz der Tatsache, dass es hier viele Antworten gibt, bietet keine von ihnen ein Verständnis dafür, wie sich die Ordnerstruktur der iOS 8.3-Simulatoren geändert hat, und bietet keine schnelle Möglichkeit, die Daten der App (Ordner "Dokumente") zu finden.

Seit iOS 8 sind die Datenspeicherordner einer App von den ausführbaren Dateien der App getrennt, während iOS 7 und niedriger dieselbe Ordnerstruktur haben. Der einzige Unterschied besteht darin, dass alle Simulatoren (verschiedene Typen und Versionen) vorhanden sind jetzt in einem großen Ordner.

Der Pfad zu einem iOS 8,7,6-Simulator ist also folgender:

~/Library/Developer/CoreSimulator/Devices

Jeder Simulator ist jetzt in einem Ordner mit einer eindeutigen Kennung enthalten, die sich bei jedem Zurücksetzen eines Simulators ändert.

Sie finden die Identifierfür jedes Ihrer Geräte und Simulatoren unter Xcode > Window > Devices(die ersten 3 oder 4 Zeichen der Kennung sind mehr als genug, um sich zu erinnern).

Um die zu finden, auf der Sie Ihre App (s) installiert haben, schauen Sie sich Ihre Run scheme > devices(Bildschirm 2) an.

Bildschirm 1

Bildschirm 2

Nachdem Sie Ihren Simulator identifiziert haben, ist die Ordnerstruktur je nach Version sehr unterschiedlich:

Unter iOS 8 befinden sich die ausführbare Datei und der Datenordner einer App in verschiedenen Ordnern:

Ausführbar : ~/Library/Developer/CoreSimulator/Devices/[simID]/data/Containers/Bundle/Application/[appID]

Datenordner : ~/Library/Developer/CoreSimulator/Devices/[simID]/data/Containers/Data/Application/[appID]/

Ordner "Dokumente" : ~/Library/Developer/CoreSimulator/Devices/[simID]/data/Containers/Data/Application/[appID]/Documents

Unter iOS 7 und darunter ist die Ordnerstruktur dieselbe wie zuvor. Denken Sie jedoch daran, dass sich jetzt jeder Simulator im selben Ordner befindet (siehe oben).


13

Wenn Ihre App CoreData verwendet, besteht ein nützlicher Trick darin, mit dem Terminal nach dem Namen der SQLite-Datei zu suchen.

find ~ -name my_app_db_name.sqlite

In den Ergebnissen werden die vollständigen Dateipfade zu allen Simulatoren aufgelistet, auf denen Ihre App ausgeführt wurde.

Ich wünschte wirklich, Apple würde einfach eine Schaltfläche zum iOS Simulator-Dateimenü hinzufügen, z. B. "Ordner im Finder anzeigen".


1
Ich muss das Terminal lieben.
Kakubei

2
Dies scheint ein ziemlich langer Weg zu sein, dies zu finden. Durchsuchen Sie Ihr gesamtes Home-Verzeichnis?
Shortstuffsushi

11

Es ist richtig, dass wir uns den Pfad ~ / Library / Developer / CoreSimulator / Devices / ansehen müssen.

Das Problem, das ich sehe, ist, dass sich der Pfad jedes Mal ändert, wenn ich die App starte. Der Pfad enthält nach der Anwendungszeichenfolge einen weiteren Satz langer IDs, die sich jedes Mal ändern, wenn ich die App ausführe. Dies bedeutet im Grunde, dass meine App beim nächsten Ausführen keine zwischengespeicherten Daten hat.


Ich sehe das auch.
Nathan Jones

5
Die Pfade ändern sich nie, wenn Sie die App ausführen. Es gibt einen Pfad pro Gerät, das Sie in Ihrer Bibliothek haben. Führen Sie 'xcrun simctl list' aus, um die Liste der Geräte und ihrer UDIDs anzuzeigen und festzustellen, welches Unterverzeichnis Sie berücksichtigen sollten.
Jeremy Huddleston Sequoia

7

Mit der Einführung von CoreSimulator in Xcode 6.0 sind die Datenverzeichnisse pro Gerät und nicht pro Version. Das Datenverzeichnis ist ~ / Library / Developer / CoreSimulator / Devices // Daten, die aus der 'xcrun simctl list' ermittelt werden können.

Beachten Sie, dass Sie ~ / Library / Application Support / iPhone Simulator und ~ / Library / Logs / iOS Simulator sicher löschen können, wenn Sie nicht vorhaben, ein Rollback auf Xcode 5.x oder früher durchzuführen.


Vielen Dank, dass Sie eine Antwort gegeben haben, die wahrscheinlich als maßgeblich angesehen werden kann. Gibt es eine einfache Möglichkeit (außer die Protokollierung innerhalb der App), den Standort einer bestimmten App in einem Simulator zu finden?
Zwei

Entschuldigung, nein, aber ich habe hier und in den Foren einige Anfragen dazu gesehen. Bitte senden
Jeremy Huddleston Sequoia

7

Unter iOS 9.2 und Xcode 7.2 öffnet das folgende Skript den Ordner "Dokumente" der zuletzt installierten Anwendung auf dem zuletzt verwendeten Simulator.

cd ~/Library/Developer/CoreSimulator/Devices/
cd `ls -t | head -n 1`/data/Containers/Data/Application 
cd `ls -t | head -n 1`/Documents
open .

Um ein einfach ausführbares Skript zu erstellen, fügen Sie es in eine Automator-Anwendung mit "Shell-Skript ausführen" ein:

Führen Sie das Shell-Skript in der Automator-Anwendung aus


6

Update: Xcode 11.5 • Swift 5.2

if let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.path {
    print(documentsPath)   // "var/folder/.../documents\n" copy the full path
}

Gehen Sie zu Ihrem Finder, drücken Sie Befehl-Umschalt-g (oder Gehe zu> Gehe zu Ordner ... unter der Menüleiste) und fügen Sie dort den vollständigen Pfad "var / folder /.../ documents" ein und drücken Sie go.


4

Versuchen ~/Library/Developer/CoreSimulator/Devices/


4

Ich hatte das gleiche Problem, als ich den vollständigen Pfad mit CoreData gespeichert habe. Beim Abrufen des vollständigen Pfads wird null zurückgegeben, da die UUID des Dokumentordners bei jedem Neustart der App unterschiedlich ist. Folgendes ist meine Entschließung:

  1. Stellen Sie sicher, dass nur der relative Pfad des Dokuments / der Datei in CoreData gespeichert wird. Speichern Sie beispielsweise "Files / image.jpg" anstelle von "/Users/yourname/.../Applications/UUID/Document/Files/image.jpg".
  2. Verwenden Sie Folgendes, um den Speicherort des App-Dokuments abzurufen:

    [[[NSFileManager defaultManager] URLsForDirectory: NSDocumentDirectory inDomains: NSUserDomainMask] lastObject];

  3. Verketten Sie sowohl # 2 als auch # 1, um den vollständigen Pfad des Dokuments / der Datei zu erhalten, die Sie abrufen möchten.
Sie können auf Apple Developer Note verweisen: https://developer.apple.com/library/ios/technotes/tn2406/_index.html


3

Wo befindet sich das Dokumentenverzeichnis für den iOS 8 Simulator?

Möglicherweise haben Sie bemerkt, dass sich der iPhone Simulator mit Xcode 6 und damit natürlich auch mit dem Pfad zum Dokumentenverzeichnis Ihrer simulierten Apps geändert hat. Manchmal müssen wir uns das ansehen.

Es ist nicht mehr so ​​einfach, diesen Pfad zu finden wie früher: Bibliothek / Anwendungsunterstützung / iPhone Simulator / 7.1 / Anwendungen / gefolgt von einer kryptischen Nummer, die Ihre App darstellt.

Ab Xcode 6 und iOS 8 finden Sie es hier: Bibliothek / Entwickler / CoreSimulator / Geräte / Kryptische Nummer / Daten / Container / Daten / Anwendung / Kryptische Nummer

http://pinkstone.co.uk/where-is-the-documents-directory-for-the-ios-8-simulator/


3

Fügen Sie in Appdelegate diesen Code ein, um Document and Cache Dir anzuzeigen:

#if TARGET_IPHONE_SIMULATOR
    NSLog(@"Documents Directory: %@", [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]);
    NSArray* cachePathArray = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    NSString* cachePath = [cachePathArray lastObject];
    NSLog(@"Cache Directory: %@", cachePath);
#endif

on Log:

Dokumentenverzeichnis: / Users / xxx / Library / Developer / CoreSimulator / Devices / F90BBF76-C3F8-4040-9C1E-448FAE38FA5E / data / Container / Daten / Anwendung / 3F3F6E12-EDD4-4C46-BFC3-58EB64D4BCCB / Documents /

Cache-Verzeichnis: / Users / xxx / Library / Developer / CoreSimulator / Devices / F90BBF76-C3F8-4040-9C1E-448FAE38FA5E / data / Container / Daten / Anwendung / 3F3F6E12-EDD4-4C46-BFC3-58EB64D4BCCB / Library / Caches


3

Wenn Sie in die App-Ordner gehen möchten, um zu sehen, was los ist, und nicht durch labyrinthische UUDIDs gehen müssen, habe ich Folgendes gemacht: https://github.com/kallewoof/plget

und damit habe ich folgendes gemacht: https://gist.github.com/kallewoof/de4899aabde564f62687

Grundsätzlich gehe ich wie folgt vor, wenn ich in den Ordner einer App gehen möchte:

$ cd ~/iosapps
$ ./app.sh
$ ls -l
total 152
lrwxr-xr-x  1 me  staff    72 Nov 14 17:15 My App Beta-iOS-7-1_iPad-Retina.iapp -> iOS-7-1_iPad-Retina.dr/Applications/BD660795-9131-4A5A-9A5D-074459F6A4BF
lrwxr-xr-x  1 me  staff    72 Nov 14 17:15 Other App Beta-iOS-7-1_iPad-Retina.iapp -> iOS-7-1_iPad-Retina.dr/Applications/A74C9F8B-37E0-4D89-80F9-48A15599D404
lrwxr-xr-x  1 me  staff    72 Nov 14 17:15 My App-iOS-7-1_iPad-Retina.iapp -> iOS-7-1_iPad-Retina.dr/Applications/07BA5718-CF3B-42C7-B501-762E02F9756E
lrwxr-xr-x  1 me  staff    72 Nov 14 17:15 Other App-iOS-7-1_iPad-Retina.iapp -> iOS-7-1_iPad-Retina.dr/Applications/5A4642A4-B598-429F-ADC9-BB15D5CEE9B0
-rwxr-xr-x  1 me  staff  3282 Nov 14 17:04 app.sh
lrwxr-xr-x  1 me  staff   158 Nov 14 17:15 com.mycompany.app1-iOS-8-0_iPad-Retina.iapp -> /Users/me/Library/Developer/CoreSimulator/Devices/129FE671-F8D2-446D-9B69-DE56F1AC80B9/data/Containers/Data/Application/69F7E3EF-B450-4840-826D-3830E79C247A
lrwxr-xr-x  1 me  staff   158 Nov 14 17:15 com.mycompany.app1-iOS-8-1_iPad-Retina.iapp -> /Users/me/Library/Developer/CoreSimulator/Devices/414E8875-8875-4088-B17A-200202219A34/data/Containers/Data/Application/976D1E91-DA9E-4DA0-800D-52D1AE527AC6
lrwxr-xr-x  1 me  staff   158 Nov 14 17:15 com.mycompany.app1beta-iOS-8-0_iPad-Retina.iapp -> /Users/me/Library/Developer/CoreSimulator/Devices/129FE671-F8D2-446D-9B69-DE56F1AC80B9/data/Containers/Data/Application/473F8259-EE11-4417-B04E-6FBA7BF2ED05
lrwxr-xr-x  1 me  staff   158 Nov 14 17:15 com.mycompany.app1beta-iOS-8-1_iPad-Retina.iapp -> /Users/me/Library/Developer/CoreSimulator/Devices/414E8875-8875-4088-B17A-200202219A34/data/Containers/Data/Application/CB21C38E-B978-4B8F-99D1-EAC7F10BD894
lrwxr-xr-x  1 me  staff   158 Nov 14 17:15 com.mycompany.otherapp-iOS-8-1_iPad-Retina.iapp -> /Users/me/Library/Developer/CoreSimulator/Devices/414E8875-8875-4088-B17A-200202219A34/data/Containers/Data/Application/DE3FF8F1-303D-41FA-AD8D-43B22DDADCDE
lrwxr-xr-x  1 me  staff    51 Nov 14 17:15 iOS-7-1_iPad-Retina.dr -> simulator/4DC11775-F2B5-4447-98EB-FC5C1DB562AD/data
lrwxr-xr-x  1 me  staff    51 Nov 14 17:15 iOS-8-0_iPad-2.dr -> simulator/6FC02AE7-27B4-4DBF-92F1-CCFEBDCAC5EE/data
lrwxr-xr-x  1 me  staff    51 Nov 14 17:15 iOS-8-0_iPad-Retina.dr -> simulator/129FE671-F8D2-446D-9B69-DE56F1AC80B9/data
lrwxr-xr-x  1 me  staff    51 Nov 14 17:15 iOS-8-1_iPad-Retina.dr -> simulator/414E8875-8875-4088-B17A-200202219A34/data
lrwxr-xr-x  1 me  staff   158 Nov 14 17:15 org.cocoapods.demo.pajdeg-iOS-8-0_iPad-Retina.iapp -> /Users/me/Library/Developer/CoreSimulator/Devices/129FE671-F8D2-446D-9B69-DE56F1AC80B9/data/Containers/Data/Application/C3069623-D55D-462C-82E0-E896C942F7DE
lrwxr-xr-x  1 me  staff    51 Nov 14 17:15 simulator -> /Users/me/Library/Developer/CoreSimulator/Devices

Der ./app.shTeil synchronisiert die Links. Dies ist heutzutage grundsätzlich immer erforderlich, da Apps die UUID für jeden Lauf in Xcode ab 6.0 ändern. Leider sind Apps auch nach Bundle-ID für 8.x und nach App-Namen für <8 sortiert.


3

Basierend auf Ankur's Antwort, aber für uns Swift-Benutzer:

let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
println("Possible sqlite file: \(urls)")

Fügen Sie es in ViewDidLoad ein und es wird sofort nach Ausführung der App ausgedruckt.


3

Die Simulatoren befinden sich unter:

~/Library/Developer/CoreSimulator/

Hier werden sie als Verzeichnisse mit UUID-Namen aufgelistet. Verwenden Sie die Sortierung nach 'Änderungsdatum', um das neueste zu finden. Innen navigieren zu:

/data/Containers/Data/Application/

Hier erhalten Sie eine Liste aller Anwendungen auf diesem Gerät. Sie können dies erneut sortieren, um die neueste App zu erhalten.

HINWEIS: Xcode ändert den Verzeichnisnamen jedes Mal, wenn Sie die App ausführen. Verlassen Sie sich daher nicht darauf, Alias ​​/ Verknüpfungen auf dem Desktop zu erstellen.

Der einfachste Weg ist es, die App nutzen hier , was alles automatisch tut.


2

Das Simulatorverzeichnis wurde mit Xcode 6 Beta nach ...

 ~/Library/Developer/CoreSimulator

Das Durchsuchen des Verzeichnisses in den Ordner "Dokumente" Ihrer App ist etwas schwieriger, z.

~/Library/Developer/CoreSimulator/Devices/4D2D127A-7103-41B2-872B-2DB891B978A2/data/Containers/Data/Application/0323215C-2B91-47F7-BE81-EB24B4DA7339/Documents/MyApp.sqlite

2

Der beste Weg, um den Pfad zu finden, ist über Code.

Mit Swift, fügen Sie einfach den Code unten in der Funktion Anwendung in Ihrem AppDelegate.swift

let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let documentsPath = paths.first as String
println(documentsPath)

Informationen zum Obj-C-Code finden Sie in der Antwort von @Ankur


2

Für Swift 3.x.

if let documentsPath = FileManager.default.urls(for:.documentDirectory, in: .userDomainMask).first?.path {
        print("Documents Directory: " + documentsPath)
    }

1

iOS 11

if let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory,
                                                           .userDomainMask,
                                                           true).first {
    debugPrint("documentsPath = \(documentsPath)")
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.